Compare commits

...

34 Commits

Author SHA1 Message Date
Andrea c0bfb0b0fe
linux munmap: fix comment and simplify memory free. (PR #1293) 2024-04-26 21:53:09 +01:00
tomcw 664d7c2d86 Phasor card: in native mode, writes to SSI263 regs 0,1 & 2 all clear the SSI263 IRQ. (#1197) 2024-04-13 13:23:10 +01:00
TomCh b00c9b0d3f
Fix for alloc of 'memimage': so that two adjacent 64K regions map to same physical 64K region. Fixes #1285. (PR #1286)
Includes platform-specific code for both Windows & Linux.
Co-authored-by: @audetto
2024-04-01 10:20:43 +01:00
tomcw d3ff855f2d Minor: replace IS_APPLE2 macro with function 2024-03-28 22:36:34 +00:00
TomCh 6b4f3060c4
Update README.md 2024-03-23 15:17:36 +00:00
tomcw ba13412e27 Save-state: fix for loading LC from older versions 2024-03-23 10:45:07 +00:00
tomcw 105537e4e0 Fix 'bad dynamic cast' for when slot-0 is empty 2024-03-22 23:16:37 +00:00
tomcw abfdb8e0db 1.30.18.0: History.txt 2024-03-22 22:30:26 +00:00
TomCh 10bf60e149
Support an extra Saturn card in slot 3 and for all Apple II models. (#1279, PR #1284)
. Command line config only, and only permitted in slot 3 for now.
. Save-state Unit v9: Extended: memory (added 'Last Slot to Set Main Mem LC', 'MMU LC Mode').
. Add LanguageCardManager class.
2024-03-22 21:36:50 +00:00
tomcw 40bf9cd2d3 6502/65C02: Fix JSR for edge-case where JSR ABS16 is on stack and SP points to ABS16! (#1257)
. Add CPU unit-tests
2024-03-03 11:30:13 +00:00
tomcw 9c1304686c Add (debug) cmd line switch: -hdc-firmware-v1 (#1277)
. use this to force all attached HDCs to use the old v1 firmware
2024-03-02 21:16:28 +00:00
tomcw 25ce998c9a Resource: bump (c) year 2024-03-02 17:14:49 +00:00
tomcw ff8f1b21c7 Updates to AppleWin.chm:
. Update Acknowledgements (Robocom dongles)
. Uthernet II: improve 'Virtual DNS' description
2024-03-02 17:02:15 +00:00
Andrea a85c9bfc07
Uthernet2: use fcntl for better compatibility with MacOS. (#1269) 2024-03-02 12:34:38 +00:00
tomcw 972ab3998b Help Troubleshooting: restore the note about resetting config to default (and move to top of page). (#918) 2024-03-02 12:11:10 +00:00
tomcw e41478be0b Help Troubleshooting: add a note about .po floppy images that don't boot 2024-02-28 21:50:56 +00:00
tomcw a16a134ae5 Robocom Interface Module dongle: use actual values for 1000 & 1500 series dongles. (#1247) 2024-02-24 20:12:06 +00:00
tomcw 0f7bec509a Zip file support: avoid double-free if there's a problem with the zip file (eg. no valid image type) 2024-02-18 21:03:32 +00:00
Peter Ibbotson 2cbecdda68
ProDOS HDD Controller firmware returns volume size in Y:X for STATUS command (PR #1272)
* New command line option to force a size for autoexpanding use.
* New HDD Controller firmware uses a separate v2 name and folder.
* Harddisk.cpp now loads HDD Controller firmware v2 by default.
* Save-state for 'Generic HDD' bumped to v4
2024-02-06 21:32:30 +00:00
TomCh 001f04c706
Phasor in Echo+ mode: fix the chip-select polarity (PR #1276) 2024-02-03 21:07:29 +00:00
TomCh 53fddfe41a
Fix SD Music card for mb-audit v1.50 (PR #1275)
. Fix for mb-audit's TestAYReadHiZ (bus-state wasn't being set correctly for SD Music's 6522 accessed via $Cs8x)
2024-01-30 19:48:34 +00:00
tomcw fbc22fa566 Fix for reading $C01x for Apple II and II+ models. (#1261) 2024-01-28 21:31:42 +00:00
tomcw 56cc779351 MegaAudio card: Reads of 6522 IRB & IRA always return 0x00
. generalise the implementation: move AY READ logic down to 6522 read
2024-01-21 20:43:02 +00:00
tomcw 43daabec54 MegaAudio card: AY READ always drives bus with 0x00 2024-01-21 15:03:33 +00:00
tomcw fcd216bb45 Speaker: should be dual-mono (not silent on left channel). (#1159) 2024-01-14 15:51:12 +00:00
tomcw d0cc5b2808 Updates to AppleWin.chm:
. Add a note about '-s6 empty' and how to restore the diskii card. (#104)
. Add a troubleshooting note about how to reset config to default. (#918)
2024-01-13 18:20:02 +00:00
tomcw 78ee8101d5 Speaker: change from mono to stereo sound buffer (but left channel silent).
Possible fix for #1159.
2024-01-13 17:55:57 +00:00
tomcw c022bbde72 Load save-state: Remove superfluous call to UpdatePaging() in MemLoadSnapshot(). (#1267) 2024-01-13 12:48:21 +00:00
tomcw 0888a25710 Small refactor for AY(s) driving bus for 6522 PortA 2024-01-06 18:20:33 +00:00
Andrea 4ab60cebf8
Serial comms: compilation in Linux (PR #1249)
* SerialComms: compilation in linux.
1) static variables must be defined (otherwise it becomes optimisation-dependent)
2) ensure the socket is closed if an error is detected

* SerialComms: avoid issues with order of global variables.
GetFrame() and the GetCardMgr() use global variables and the order of their destructor is not well defined.
CSuperSerialCard::CommTcpSerialCleanup() is called as part of the CardManager's destructor.
2024-01-06 14:50:10 +00:00
Andrea 7497aa9923
Minor bugs in SSI263 (PR #1270)
SSI263: avoid assertion in DSVoiceStop when running with -no-mb.
2024-01-05 22:41:38 +00:00
tomcw 69151a2aff MB/Phasor: support AY(s) driving bus for 6522 PortA (ie. for the PSG READ function)
. Bump MB/Phasor save-state unit to v11
. Tested against mb-audit v1.50
2024-01-03 21:22:25 +00:00
TomCh cf0ae53018
Update README.md
Latest stable release: 1.30.16.0
2024-01-01 21:23:58 +00:00
tomcw fea4173b43 Debugger: CmdOutputRun:
. for scripts that can be loaded: output script pathname to console.
. for scripts that can't be loaded: don't truncate pathname that's output to console.
2024-01-01 11:11:51 +00:00
51 changed files with 1358 additions and 329 deletions

View File

@ -16,15 +16,16 @@ Peripheral cards and add-on hardware supported:
- RGB cards: Apple's Extended 80-Column Text/AppleColor Adaptor Card and 'Le Chat Mauve' Féline.
- CP/M SoftCard
- Uthernet I and II (ethernet cards)
- Language Card and Saturn 64/128K for Apple II/II+
- Language Card and Saturn 64/128K for Apple II/II+ (and Saturn 128K for any Apple II in slot 3)
- 4Play and SNES MAX joystick cards
- VidHD card (functionality limited to IIgs' Super Hi-Res video modes)
- No Slot Clock (NSC)
- Game I/O Connector copy protection dongles
Download latest (stable) release: [AppleWin v1.30.14.1](https://github.com/AppleWin/AppleWin/releases/download/v1.30.14.1/AppleWin1.30.14.1.zip)
Download latest (stable) release: [AppleWin v1.30.17.0](https://github.com/AppleWin/AppleWin/releases/download/v1.30.17.0/AppleWin1.30.17.0.zip)
Release Notes: [v1.30.14.0](https://github.com/AppleWin/AppleWin/releases/tag/v1.30.14.0)
Release Notes: [v1.30.17.0](https://github.com/AppleWin/AppleWin/releases/tag/v1.30.17.0)
Building

View File

@ -9,6 +9,18 @@ https://github.com/AppleWin/AppleWin/issues/new
Tom Charlesworth
1.30.18.0 - 23 Mar 2023
-----------------------
- [Change #1272] New HDC firmware v2 to support returning HDD size in Y:X for status call. [@peteri]
- [Change #1277] Add (debug) cmd line switch to revert to HDC firmware v1: -hdc-firmware-v1.
- [Change #1279] Support Saturn RAM card in slot 3 for any Apple II model (for Robo Systems' RoboCAD 2).
- [Change #1159] Speaker: change from mono to stereo sound buffer.
- [Change #1247] Robocom's Interface Module dongle: use actual values for 1000 & 1500 series dongles.
- [Bug #1261] Fix for $C010 which should not read the keyboard latch on Apple II and Apple II+.
- [Bug #1257] 6502/65C02: Fix JSR for edge-case where JSR ABS16 is on stack and SP points to ABS16!
- [Bug #1276] Phasor in Echo+ mode: fix the chip-select polarity.
1.30.17.0 - 31 Dec 2023
-----------------------
- [Bug #1262] Fix loading save-state for RAMWorks III card with 2 or more aux 64K banks.

11
firmware/HDDv2/build.bat Normal file
View File

@ -0,0 +1,11 @@
@REM ACME only ever returns 0!
acme.exe hddrvr-v2.a65
@IF %ERRORLEVEL% NEQ 0 goto error
copy hddrvr-v2.bin ..\..\resource
@goto end
:error
@echo "ACME failed"
:end

View File

@ -0,0 +1,358 @@
;AppleWin : An Apple //e emulator for Windows
;
;Copyright (C) 1994-1996, Michael O'Brien
;Copyright (C) 1999-2001, Oliver Schmidt
;Copyright (C) 2002-2005, Tom Charlesworth
;Copyright (C) 2006-2012, Tom Charlesworth, Michael Pohoreski
;
;AppleWin is free software; you can redistribute it and/or modify
;it under the terms of the GNU General Public License as published by
;the Free Software Foundation; either version 2 of the License, or
;(at your option) any later version.
;
;AppleWin is distributed in the hope that it will be useful,
;but WITHOUT ANY WARRANTY; without even the implied warranty of
;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;GNU General Public License for more details.
;
;You should have received a copy of the GNU General Public License
;along with AppleWin; if not, write to the Free Software
;Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
;
; Description: Firmware for harddisk card
;
; Author: Copyright (c) 2005, Robert Hoem
;
; Modified by Tom Charlesworth:
; . Updated so it can be assembled by ACME 0.97
; . Fixed so that ProDOS entrypoint is $c70a (26 Dev 2007) (Bug #12723)
; . Modified to support Apple Oasis' entrypoint: $c761 (8 Sept 2012) (Feature #5557)
; . Added support for SmartPort entrypoint (20 Oct 2012)
; - EG. "Prince of Persia (Original 3.5 floppy for IIc+).2mg"
; . GH#370 (Robert Hoem, 27 Oct 2016):
; . Added a check against open-apple during boot to route boot to slot 6
; . This happens after the first two blocks are loaded from the HD.
; . GH#319: SmartPort return address wrong when crossing page
; . GH#996: Make code slot-independent (so HDD controller card can go into any slot)
; . Moved the 512-byte block read into AppleWin's HDD emulation (to mirror the block write command)
; TODO:
; . Remove support for Entrypoint_Cs46 (old AppleWin) & Entrypoint_Cs61 (Apple Oasis)
; - provide a utility to convert these to use Entrypoint_ProDOS
; . Check SmartPort: Is it OK to trash Y and $42,..,$47 ?
;
!cpu 6502 ; Compatible with all Apple2's
!to "hddrvr-v2.bin", plain
!sl "hddrvr-v2.labels"
; constants
hd_execute = $c080
hd_status = $c081 ; b7=busy, b0=error
hd_command = $c082
hd_unitnum = $c083
hd_memblock = $c084
hd_diskblock = $c086
;hd_nextbyte = $c088 ; legacy read-only port (still supported by AppleWin)
hd_imgsizelo = $c089
hd_imgsizehi = $c08a
; Notes on accesses to I/O registers:
; . ROR ABS16,X and ROL ABS16,X - only used for $C081+s*$10 STATUS register:
; 6502: double read (old data), write (old data), write (new data). The writes are harmless as writes to STATUS are ignored.
; 65C02: double read (old data), write (new data). The write is harmless as writes to STATUS are ignored.
; . STA ABS16,X does a false-read. This is harmless for writable I/O registers, since the false-read has no side effect.
command = $42
unitnum = $43
memblock = $44
diskblock = $46
slot6 = $C600
OS = $0801
BUTTON0 = $C061
;======================================
!zone code
*= $0000 ; org $0000 - position-independent code, so doesn't matter (but the other fixed org positions need to be on the same page)
; The Autoboot rom will call this.
; This is also the entry point for such things as IN#7 and PR#7
start
; Autoboot and ProDOS look at the following few opcodes to detect block devices
; NB. $Cn07 should be $00 for a SmartPort Interface, but changing this means that it won't autoboot on ][, ][+ and unenhanced IIe.
; . ref: http://www.1000bit.it/support/manuali/apple/technotes/udsk/tn.udsk.2.html
lda #$20
lda #$00
lda #$03
lda #$3C
bne Bootstrap
Entrypoint_ProDOS ; $Cn0A - ProDOS entrypoint
sec
bcs Entrypoint
Entrypoint_SmartPort ; $Cn0D - SmartPort entrypoint
clc
Entrypoint ; $Cn0E - entrypoint?
bcs GetSlotInX ; C=1: GetSlotInX -> cmdproc
; C=0: fall through to SmartPort...
;======================================
; TODO: Is it OK to trash Y and $42,..,$47 ?
; Pre: C=0, X = Slot# << 4
SmartPort ; SmartPort -> GetSlotInX -> cmdproc
pla
sta $46
adc #3 ; Pre: C=0, Post: C=0 or 1
tay
pla
sta $47 ; ($46) = &cmd_hdr
adc #0
pha
tya
pha ; (sp).w += 3
ldy #1
lda ($46),y ; cmd
sta $42
iny
lda ($46),y ; param_l
sta $45
iny
lda ($46),y ; param_h
sta $46
ldy #1 ; skip paramLength (assume it's #$03)
lda ($45),y ; unit
sta $43
iny
lda ($45),y ; memblock_l
sta $44
iny
lda ($45),y ; memblock_h
pha
iny
lda ($45),y ; diskblock_l
pha
iny
bne SmartPort2
;======================================
; 2 unused bytes
@checkCs46
*= $0046 ; org $0046
!warn "Cs46 padding = ", * - @checkCs46
Entrypoint_Cs46 ; Old f/w 'cmdproc' entrypoint
; Keep this for any DOSMaster HDD images created with old AppleWin HDD f/w.
; DOSMaster hardcodes the entrypoint addr into its bootstrapping code:
; - So DOSMaster images are tied to the HDD's controller's f/w
sec
bcs Entrypoint ; or directly to GetSlotInX
;======================================
Bootstrap
; Lets check to see if there's an image ready
; Slot n, disk 1
clc
bcc GetSlotInX ; Post: X = Slot# << 4
Bootstrap2
lda #$00
sta hd_unitnum,x ; b7=0 => disk 1
sta hd_command,x
lda hd_execute,x
ror hd_status,x ; Post: C=0 or 1
bcc hdboot
; no image ready, boot diskette image instead
BootSlot6
jmp slot6
;======================================
; 2 unused bytes
@checkCs61
*= $0061 ; org $0061
!warn "Cs61 padding = ", * - @checkCs61
Entrypoint_Cs61 ; Apple Oasis HDD controller entrypoint
; Keep this for any DOSMaster HDD images created with Apple Oasis HDD f/w.
; DOSMaster hardcodes the entrypoint addr into its bootstrapping code:
; - So DOSMaster images are tied to the HDD's controller's f/w
sec
bcs Entrypoint ; or directly to GetSlotInX
;======================================
; image ready. Lets boot from it.
; we want to load block 1 from disk 1 to $800 then jump there
; Pre: X = Slot# << 4
; C = 0
hdboot
lda #$0
sta unitnum ; b7=0 => disk 1
sta memblock
sta diskblock
sta diskblock+1
lda #$8
sta memblock+1
lda #$1
sta command
bne cmdproc
hdboot2
bcs BootSlot6
bit BUTTON0 ; button 0 pressed?
bmi BootSlot6
; Pre: X = Slot# << 4
jmp OS
;======================================
SmartPort2
lda ($45),y ; diskblock_h
sta $47
pla
sta $46
pla
sta $45
sec
; fall through...
;======================================
; Pre:
; C=0 => via Bootstrap
; C=1 => via Entrypoint / SmartPort2
; Post:
; X = Slot# << 4
GetSlotInX
php
sei ; disable ints, in case an int handler races our $0000/RTS and stack accesses!
; NB. need RAM that's guaranteed to be both read & writeable:
; . can't use $0200-$BFFF, due to eg. RAMRD=0/RAMWRT=1 combination
; . can't use LC as ROM might be enabled.
; So use ZP (specifically $0000) as whatever the state of ALTZP, both read & write will be to the same physical memory location.
lda $00 ; save $00
ldx #$60 ; opcode RTS
stx $00
jsr $0000 ; RTS immediately (NB. can't use $FF58, since LC RAM may be switched in)
sta $00 ; restore $00
tsx
lda $0100,x ; $Cn
asl
asl
asl
asl
tax ; X=$n0
plp ; + restore int status
bcc Bootstrap2
; otherwise fall through for Entrypoint / SmartPort...
;--------------------------------------
; entry point for ProDOS' block driver
; simple really. Copy the command from $42..$47
; to our I/O ports then execute command
; Pre:
; C=0 => via Bootstrap (hdboot)
; C=1 => via GetSlotInX (eg. Entrypoint / SmartPort2)
; X = Slot# << 4
; Post:
; C = hd_status.b0
; A = result of hd_execute
; Read or write command
; X = Slot# << 4
; Y = command
; Status command
; X = low byte of disk size
; Y = high byte of disk size
cmdproc
php
lda command
sta hd_command,x
lda unitnum
sta hd_unitnum,x
lda memblock
sta hd_memblock,x
lda memblock+1
sta hd_memblock+1,x
lda diskblock
sta hd_diskblock,x
lda diskblock+1
sta hd_diskblock+1,x
lda hd_execute,x ; A = result of hd_execute (NB. instantaneous 512 byte r/w!)
- rol hd_status,x ; b7=busy doing DMA?
bcs -
plp ; restore C from start of cmdproc
bcs done
ror hd_status,x ; Post: C=0 or 1
lda #0
beq hdboot2
done
ror hd_status,x ; Post: C=0 or 1
ldy command ; Was it status
beq size ; yes, fill in the values
rts ; no, go home
size
pha ; Save result
ldy hd_imgsizehi,x ; Get high byte of size
lda hd_imgsizelo,x ; Get low byte of size
tax ; Transfer into X
pla ; Get back status call result
rts
;======================================
; 18 unused bytes
!zone data
; $CsFE = status bits (BAP p7-14)
; 7 = medium is removable
; 6 = device is interruptable
; 5-4 = number of volumes (0..3 means 1..4)
; 3 = device supports Format call
; 2 = device can be written to
; 1 = device can be read from (must be 1)
; 0 = device status can be read (must be 1)
; $C7 = Removable, Interruptable, #Volumes=1, Supports write/read/status
; $D7 = Removable, Interruptable, #Volumes=2, Supports write/read/status
; $BF = Removable, Interruptable, #Volumes=4, Supports format/write/read/status (KEGS / IIGS)
; datablock. This starts near the end of the firmware (at offset $FB)
;; data
@checkCsFB
*= $00FB ; org $00FB
!warn "CsFB padding = ", * - @checkCsFB
!byte $00 ; Smart port ID Type byte
!word $0000 ; how many blocks are on the device. Zero means use status call
!byte $D7 ; specifics about the device (number of drives, read/write/format capability, etc)
!byte <Entrypoint_ProDOS ; entry point offset for ProDOS (must be $0a)

Binary file not shown.

View File

@ -43,12 +43,16 @@
-s0 &lt;saturn|saturn64|saturn128&gt;<br>
Insert a Saturn 64K or Saturn 128K card into slot 0 in the Apple II or II+ machines (or similar clone).<br>
Where -s0 saturn is an alias for -s0 saturn128.<br><br>
-s3 &lt;saturn|saturn128&gt;<br>
Insert a Saturn 128K card into slot 3 in any Apple II machine.<br>
Use this configuration for Robocom Ltd's (Robo Systems') CAD software in combination with the Interface Module protection dongle in the Game I/O Connector.<br><br>
-s0 &lt;languagecard|lc&gt;<br>
Insert an Apple 16K Language Card into slot 0 in the original Apple II and use the F8 auto-start ROM.<br>
NB. The Apple II+ already defaults to having a Language Card, so this switch is not required.<br><br>
-s&lt;N&gt; empty<br>
Remove any card from slot N (N=1-7).<br>
NB. '-s7 empty' is useful to allow a floppy disk to boot from slot 6, drive 1. Use in combination with -d1.<br><br>
NB. '-s7 empty' is useful to allow a floppy disk to boot from slot 6, drive 1. Use in combination with -d1.<br>
NB. '-s6 empty' persists this state to the Registry and there's currently no GUI option to re-insert a Disk II controller card into slot 6. So you must use '-s6 diskii' to re-enable the card in slot 6.<br><br>
-s1 parallel<br>
Insert a parallel printer card into slot 1.<br><br>
-s2 ssc<br>
@ -68,6 +72,8 @@
Insert a hard disk controller card into slot 5 or 7.<br><br>
-d1-disconnected, -d2-disconnected<br>
Disconnect drive-1 and/or drive-2 from the Disk II controller card in slot 6.<br><br>
-harddisknumblocks &lt;number of ProDOS blocks&gt;<br>
Set the number of blocks returned by a ProDOS status call. Use -harddisknumblocks 32767 to have the same autoexpanding behavior as older AppleWin versions.<br><br>
-no-nsc<br>
Remove the No-Slot clock (NSC).<br><br>
-r &lt;number of pages&gt;<br>
@ -203,9 +209,9 @@
<br>
-wav-speaker &lt;file.wav&gt;<br>
Save the speaker audio to a .wav file.<br>
Warning: there's no file size limit, so it just keeps saving until AppleWin exits (~5MB per minute).<br>
Warning: there's no file size limit, so it just keeps saving until AppleWin exits (~10MB per minute).<br>
<br>
Save the Mockingboard audio to a .wav file.<br>
Save the Mockingboard audio (but not speech) to a .wav file.<br>
-wav-mockingboard &lt;file.wav&gt;<br>
Warning: there's no file size limit, so it just keeps saving until AppleWin exits (~10MB per minute).<br>
<br>
@ -223,5 +229,11 @@
<br><br>
-screenshot-and-exit<br>
For testing. Use in combination with -load-state.<br><br>
-hdc-firmware-v1<br>
Force all attached hard disk controllers to use the old v1 firmware (as per pre-AppleWin 1.30.17).
<ul>
<li>NB. Switch likely to be removed after a few releases.</li>
</ul>
<br><br>
</body>
</html>

View File

@ -10,6 +10,16 @@
<hr size="4">
<p style="FONT-WEIGHT: bold">Here is a list of issues and possible solutions:</p>
How to reset AppleWin back to the default settings:
<ul>
<li>Within the AppleWin install folder there is the file: DELREG.INF.</li>
<ul>
<li>Windows 10/11: From Explorer, right click on this file, then select: Install</li>
</ul>
<li>This will remove all AppleWin configuration from the Registry.</li>
</ul>
<br>
Uthernet network card not working.
<ul>
<li>There are problems when running with certain firewalls (eg. ZoneAlarm).
@ -19,6 +29,7 @@
</li>
</ul>
<br>
AppleWin doesn't startup.
<ul>
<li>It could be being blocked by your anti-virus software. There is a known problem with Constant Guard Protection Suite.
@ -28,6 +39,7 @@
</li>
</ul>
<br>
When using a non-US (or UK) keyboard, certain keys can't be typed.
<ul>
<li>AltGr (or right Alt) needs to be used to type certain keys, eg:
@ -46,12 +58,14 @@
<li>The workaround is to use the `-no-hook-alt` command line switch and configure Joystick 1 = "Keyboard (numpad)"; and then use the '0' and '.' keys for Open/Solid Apple.</li>
</ul>
<br>
No Apple II speaker sound, but Mockingboard sound is working!
<ul>
<li>Check the Volume Control for the Speaker, under the <a href="cfg-sound.html">Configuration->Sound tab</a>.</li>
<li>Try installing the Realtek-specific audio driver supplied by ASUS (as opposed to the default Win10 driver).</li>
</ul>
<br>
Color (RGB Card/Monitor) and title compatibility issues:
<ul>
<li>Dragon Wars: double hi-res bit7 needs inverting, so use the -rgb-card-invert-bit7 command line switch.</li>
@ -59,6 +73,7 @@
<li>Apple's AppleColor card or Video7's RGB-SL7 card: corruption for titles/demos that accidentally switch to foreground/background hi-res mode, eg. French Touch DIGIDREAM demo.</li>
</ul>
<br>
Can't switch to 2x windowed mode:
<ul>
<li>If the resolution isn't high enough to support 2x windowed mode, then AppleWin will refuse to switch.</li>
@ -66,18 +81,48 @@
<ul>
<li>Windows 7 requires: 1181 x 808</li>
<li>Windows 10 (1909) requires: 1181 x 818</li>
<li>Windows 11 (23H2) requires: 1177 x 805</li>
</ul>
</ul>
<br>
Can't set bookmark 0 in the debugger / CTRL+SHIFT+0 not working:
<img style="FLOAT: right;" src="img/Change Key Sequence.png" alt="Configuration settings" hspace="5" vspace="5">
<ul>
<li>From Control Panel...".</li>
<li>Windows 7: From "Control Panel...":</li>
<ul>
<li>Windows 7: Change keyboards or other input methods -> 'Keyboards and Languages' tab -> Change Keyboards -> 'Advanced Key Settings' -> Change Key Sequence...</li>
</ul>
</ul>
<ul>
<li>Windows 10: From "Control Panel...":</li>
<ul>
<li>Windows 10: Typing -> Advanced Keyboard Settings -> Input language hot keys -> Change Key Sequence...</li>
</ul>
</ul>
<ul>
<li>Windows 11: From "Settings":</li>
<ul>
<li>Time & Language -> Typing -> Advanced Keyboard Settings -> Input language hot keys -> Change Key Sequence...</li>
</ul>
</ul>
<ul>
<li>Finally set "Switch Keyboard Layout" to "Not Assigned".</li>
</ul>
<br>
.po floppy image doesn't boot and may crash to the monitor.
<ul>
<li>Try renaming with a ".do" or ".dsk" extension.</li>
<li>Or you can use a utility like CiderPress to convert them to proper .po disks and then they will work correctly in emulators and with ProDOS block devices.</li>
<li> Details:</li>
<ul>
<li>".do" and ".po" specify a specific order.</li>
<li>These non-booting .po images are with DOS-order sectors.</li>
</ul>
</ul>
</body>
</html>

View File

@ -29,5 +29,6 @@
<p style="MARGIN-LEFT: 40px">Matthew D'Asaro: Game I/O Controller copy-protection dongle support</p>
<p style="MARGIN-LEFT: 40px">Erik Struiksma: SNES MAX controller mapping files</p>
<p style="MARGIN-LEFT: 40px">John Leffingwell: LFSR logic for the CodeWriter copy-protection dongle</p>
<p style="MARGIN-LEFT: 40px">Hugh Hood: loan of his Robocom Interface Module dongles</p>
</body>
</html>

View File

@ -49,7 +49,7 @@
<li>If 'W' is inverse: RAM is write enabled.
<li>If 'W' is not inverse: RAM is write protected.
<li>'rNN' will appear if a RamWorks 64K bank is active.
<li>'sNN' will appear if a Saturn 16K bank is active.
<li>'sNN' will appear if a Saturn 16K bank is active (slot 0 only).
</ul>
</ul>
</p>

View File

@ -23,6 +23,7 @@
<li>Apple model: ][, ][+, //e, Enhanced //e or clone (eg. Pravets)</li>
<li>Apple ]['s slot-0 language card: 16K Language Card, Saturn 64K or Saturn 128K</li>
<li>Apple //e's auxiliary card: 80 Column, Extended 80 Column or RamWorks III</li>
<li>Any Apple II: Saturn 128K cards in slots other than slot 0</li>
<li>Disk][ (even during r/w operation)</li>
<li>Hard disk (even during r/w operation)</li>
<li>Mockingboard & Phasor cards</li>
@ -35,6 +36,7 @@
<li>Uthernet & Uthernet II cards</li>
<li>4Play & SNES MAX joystick cards</li>
<li>VidHD card</li>
<li>Game I/O Connector copy protection dongle</li>
</ul>
The following are not yet persisted to the file:
<ul>

View File

@ -12,7 +12,7 @@
Copyright © 1994-1996, Michael O'Brien<br>
Copyright © 2001, Oliver Schmidt<br>
Copyright © 2002-2005, Tom Charlesworth<br>
Copyright © 2006-2023, Tom Charlesworth, Michael Pohoreski, Nick Westgate, Linards Ticmanis<br>
Copyright © 2006-2024, Tom Charlesworth, Michael Pohoreski, Nick Westgate, Linards Ticmanis<br>
<br>
<a href="applewin-team.html">AppleWin team</a>
<br>

View File

@ -98,7 +98,15 @@
</ul>
</P>
<P>The card implements a <A href="https://github.com/a2retrosystems/uthernet2/wiki/Virtual-W5100-with-DNS">Virtual DNS</A>
interface (not found on real hardware) for Apple II software to run without raw sockets:
this allows operation on any type of network.</P>
interface (not found on real hardware) for Apple II software to run without raw sockets: this allows operation on any type of network.
</P>
<P>Note that the Apple II application must be virtual W5100-aware (currently as of early 2024 the only application that supports this is <A href="https://github.com/oliverschmidt/A2Stream">A2Stream</A>). AppleWin will utilise DNS offloading when both AppleWin is configured to use "Virtual DNS" and the Apple II application enables the W5100 for the "Virtual DNS" mode.
</P>
<P>Enabling AppleWin for "Virtual DNS" and using with Apple II applications that don't support this is harmless (but you won't get any benefit).
</P>
<P>AppleWin's W5100 emulation returns the following depending on whether AppleWin's "Virtual DNS" is checked or not:
<li>Checked (default): W5100 register 0x28 returns 0x00 (so Apple II applications can use this to detect the virtual W5100 has virtual DNS support).</li>
<li>Unchecked: W5100 register 0x28 returns 0x28 (ie. the emulated W5100 behaves like a real W5100).</li>
</P>
</body>
</html>

View File

@ -257,7 +257,7 @@ BEGIN
CONTROL "The Free&ze's non-autostart F8 rom (Apple ][ or ][+ only)",IDC_THE_FREEZES_F8_ROM_FW,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,201,194,10
LTEXT "&Game I/O Connector:",IDC_STATIC,5,220,82,8
COMBOBOX IDC_COMBO_GAME_IO_CONNECTOR,89,218,114,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
COMBOBOX IDC_COMBO_GAME_IO_CONNECTOR,80,218,128,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
END
@ -298,7 +298,7 @@ BEGIN
VALUE "FileDescription", "Apple //e Emulator for Windows"
VALUE "FileVersion", APPLEWIN_VERSION_STR
VALUE "InternalName", "APPLEWIN"
VALUE "LegalCopyright", " 1994-2023 Michael O'Brien, Oliver Schmidt, Tom Charlesworth, Michael Pohoreski, Nick Westgate, Linards Ticmanis"
VALUE "LegalCopyright", " 1994-2024 Michael O'Brien, Oliver Schmidt, Tom Charlesworth, Michael Pohoreski, Nick Westgate, Linards Ticmanis"
VALUE "OriginalFilename", "APPLEWIN.EXE"
VALUE "ProductName", "Apple //e Emulator"
VALUE "ProductVersion", APPLEWIN_VERSION_STR
@ -332,6 +332,7 @@ IDR_DISK2_13SECTOR_FW FIRMWARE "Disk2-13sector.rom"
IDR_DISK2_16SECTOR_FW FIRMWARE "Disk2.rom"
IDR_SSC_FW FIRMWARE "SSC.rom"
IDR_HDDRVR_FW FIRMWARE "Hddrvr.bin"
IDR_HDDRVR_V2_FW FIRMWARE "Hddrvr-v2.bin"
IDR_PRINTDRVR_FW FIRMWARE "Parallel.rom"
IDR_MOCKINGBOARD_D_FW FIRMWARE "Mockingboard-D.rom"
IDR_MOUSEINTERFACE_FW FIRMWARE "MouseInterface.rom"

BIN
resource/Hddrvr-v2.bin Normal file

Binary file not shown.

View File

@ -52,6 +52,7 @@
#define IDR_APPLE2_JPLUS_VIDEO_ROM 152
#define IDR_BASE_64A_ROM 153
#define IDR_BASE64A_VIDEO_ROM 154
#define IDR_HDDRVR_V2_FW 155
#define IDC_KEYB_BUFFER_ENABLE 1005
#define IDC_SAVESTATE 1006
#define IDC_SAVESTATE_ON_EXIT 1007

View File

@ -1,4 +1,4 @@
#define APPLEWIN_VERSION 1,30,17,0
#define APPLEWIN_VERSION 1,30,18,0
#define xstr(a) str(a)
#define str(a) #a

View File

@ -43,6 +43,7 @@ void SY6522::Reset(const bool powerCycle)
memset(&m_regs, 0, sizeof(Regs));
m_regs.TIMER1_LATCH.w = 0xffff; // Some random value (but pick $ffff so it's deterministic)
// . NB. if it's too small (< ~$0007) then MB detection routines will fail!
m_isBusDriven = false;
}
CpuCreateCriticalSection(); // Reset() called by SY6522 global ctor, so explicitly create CPU's CriticalSection
@ -128,13 +129,6 @@ USHORT SY6522::SetTimerSyncEvent(BYTE reg, USHORT timerLatch)
//-----------------------------------------------------------------------------
void SY6522::UpdatePortAForHiZ(void)
{
BYTE ora = GetReg(SY6522::rORA);
ora |= GetReg(SY6522::rDDRA) ^ 0xff; // for any DDRA bits set as input (logical 0), then set them in ORA
SetRegORA(ora); // Empirically Phasor's 6522-AY8913 bus floats high (or pull-up?) if no AY chip-selected (so DDRA=0x00 will read 0xFF as input)
}
void SY6522::UpdateIFR(BYTE clr_ifr, BYTE set_ifr /*= 0*/)
{
m_regs.IFR &= ~clr_ifr;
@ -351,9 +345,11 @@ BYTE SY6522::Read(BYTE nReg)
{
case 0x00: // IRB
nValue = m_regs.ORB | (m_regs.DDRB ^ 0xff); // Input bits read back as 1's (GH#1260)
if (m_isMegaAudio) nValue = 0x00; // MegaAudio: IRB just reads as $00
break;
case 0x01: // IRA
nValue = m_regs.ORA; // NB. Inputs bits driven by AY8913
nValue = m_regs.ORA | (m_isBusDriven ? 0x00 : (m_regs.DDRA ^ 0xff)); // NB. Inputs bits driven by AY8913 if in PSG READ mode
if (m_isMegaAudio) nValue = 0x00; // MegaAudio: IRA just reads as $00
break;
case 0x02: // DDRB
nValue = m_regs.DDRB;

View File

@ -3,7 +3,7 @@
class SY6522
{
public:
SY6522(UINT slot, bool isMegaAudio) : m_slot(slot), m_isMegaAudio(isMegaAudio)
SY6522(UINT slot, bool isMegaAudio) : m_slot(slot), m_isMegaAudio(isMegaAudio), m_isBusDriven(false)
{
for (UINT i = 0; i < kNumTimersPer6522; i++)
m_syncEvent[i] = NULL;
@ -28,7 +28,6 @@ public:
void StopTimer2(void);
bool IsTimer2Active(void) { return m_timer2Active; }
void UpdatePortAForHiZ(void);
void UpdateIFR(BYTE clr_ifr, BYTE set_ifr = 0);
void UpdateTimer1(USHORT clocks);
@ -53,8 +52,9 @@ public:
USHORT GetRegT1C(void) { return m_regs.TIMER1_COUNTER.w; }
USHORT GetRegT2C(void) { return m_regs.TIMER2_COUNTER.w; }
void GetRegs(BYTE regs[SIZE_6522_REGS]) { memcpy(&regs[0], (BYTE*)&m_regs, SIZE_6522_REGS); } // For debugger
void SetRegORA(BYTE reg) { m_regs.ORA = reg; }
void SetRegIRA(BYTE reg) { m_regs.ORA = reg; }
bool IsTimer1IrqDelay(void) { return m_timer1IrqDelay ? true : false; }
void SetBusBeingDriven(bool state) { m_isBusDriven = state; }
BYTE Read(BYTE nReg);
void Write(BYTE nReg, BYTE nValue);
@ -145,6 +145,7 @@ private:
class SyncEvent* m_syncEvent[kNumTimersPer6522];
UINT m_slot;
bool m_isMegaAudio;
bool m_isBusDriven;
static const UINT kExtraMegaAudioTimerCycles = kExtraTimerCycles + 1;
};

View File

@ -94,7 +94,7 @@ static DWORD Cpu6502(DWORD uTotalCycles, const bool bVideoUpdate)
case 0x1D: ABSX_OPT ORA CYC(4) break;
case 0x1E: ABSX_CONST ASLn CYC(7) break;
case 0x1F: ABSX_CONST ASO CYC(7) break; // invalid
case 0x20: ABS JSR CYC(6) break;
case 0x20: JSR CYC(6) break; // GH#1257: not ABS
case 0x21: idx AND CYC(6) break;
case 0x22: HLT CYC(2) break; // invalid
case 0x23: idx RLA CYC(8) break; // invalid

View File

@ -94,7 +94,7 @@ static DWORD Cpu65C02(DWORD uTotalCycles, const bool bVideoUpdate)
case 0x1D: ABSX_OPT ORA CYC(4) break;
case 0x1E: ABSX_OPT ASLc CYC(6) break;
case 0x1F: NOP CYC(1) break; // invalid
case 0x20: ABS JSR CYC(6) break;
case 0x20: JSR CYC(6) break; // GH#1257: not ABS
case 0x21: idx AND CYC(6) break;
case 0x22: IMM NOP CYC(2) break; // invalid
case 0x23: NOP CYC(1) break; // invalid

View File

@ -400,10 +400,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#define INY ++regs.y; \
SETNZ(regs.y)
#define JMP regs.pc = addr;
#define JSR --regs.pc; \
#define JSR addr = *(LPBYTE)(mem+regs.pc); regs.pc++; \
PUSH(regs.pc >> 8) \
PUSH(regs.pc & 0xFF) \
regs.pc = addr;
regs.pc = addr | (*(LPBYTE)(mem+regs.pc)) << 8; /* GH#1257 */
#define LAS /*bSlowerOnPagecross = 1*/; \
val = (BYTE)(READ & regs.sp); \
regs.a = regs.x = (BYTE) val; \

View File

@ -115,21 +115,22 @@ void CardManager::InsertInternal(UINT slot, SS_CARDTYPE type)
case CT_Uthernet2:
m_slot[slot] = new Uthernet2(slot);
break;
case CT_LanguageCard:
_ASSERT(m_pLanguageCard == NULL);
if (m_pLanguageCard) break; // Only support one language card
m_slot[slot] = m_pLanguageCard = LanguageCardSlot0::create(slot);
case CT_LanguageCardIIe:
_ASSERT(slot == SLOT0);
if (GetLanguageCardMgr().SetLanguageCard(type))
m_slot[SLOT0] = GetLanguageCardMgr().GetLanguageCard();
break;
case CT_Saturn128K:
_ASSERT(m_pLanguageCard == NULL);
if (m_pLanguageCard) break; // Only support one language card
m_slot[slot] = m_pLanguageCard = new Saturn128K(slot, Saturn128K::GetSaturnMemorySize());
break;
case CT_LanguageCardIIe:
_ASSERT(m_pLanguageCard == NULL);
if (m_pLanguageCard) break; // Only support one language card
m_slot[slot] = m_pLanguageCard = LanguageCardUnit::create(slot);
if (slot == SLOT0)
{
if (GetLanguageCardMgr().SetLanguageCard(type))
m_slot[SLOT0] = GetLanguageCardMgr().GetLanguageCard();
}
else
{
m_slot[slot] = new Saturn128K(slot, Saturn128K::kMaxSaturnBanks);
}
break;
default:
@ -165,9 +166,13 @@ void CardManager::RemoveInternal(UINT slot)
m_pParallelPrinterCard = NULL;
break;
case CT_LanguageCard:
case CT_Saturn128K:
case CT_LanguageCardIIe:
m_pLanguageCard = NULL;
_ASSERT(slot == SLOT0);
GetLanguageCardMgr().SetLanguageCard(CT_Empty);
break;
case CT_Saturn128K:
if (slot == SLOT0)
GetLanguageCardMgr().SetLanguageCard(CT_Empty);
break;
case CT_Z80:
m_pZ80Card = NULL;

View File

@ -2,6 +2,7 @@
#include "Card.h"
#include "Disk2CardManager.h"
#include "LanguageCard.h"
#include "MockingboardCardManager.h"
#include "Common.h"
@ -11,7 +12,6 @@ public:
CardManager(void) :
m_pMouseCard(NULL),
m_pSSC(NULL),
m_pLanguageCard(NULL),
m_pParallelPrinterCard(NULL),
m_pZ80Card(NULL)
{
@ -53,6 +53,7 @@ public:
//
Disk2CardManager& GetDisk2CardMgr(void) { return m_disk2CardMgr; }
LanguageCardManager& GetLanguageCardMgr(void) { return m_languageCardMgr; }
MockingboardCardManager& GetMockingboardCardMgr(void) { return m_mockingboardCardMgr; }
class CMouseInterface* GetMouseCard(void) { return m_pMouseCard; }
bool IsMouseCardInstalled(void) { return m_pMouseCard != NULL; }
@ -61,8 +62,6 @@ public:
class ParallelPrinterCard* GetParallelPrinterCard(void) { return m_pParallelPrinterCard; }
bool IsParallelPrinterCardInstalled(void) { return m_pParallelPrinterCard != NULL; }
class LanguageCardUnit* GetLanguageCard(void) { return m_pLanguageCard; }
void InitializeIO(LPBYTE pCxRomPeripheral);
void Destroy(void);
void Reset(const bool powerCycle);
@ -78,10 +77,10 @@ private:
Card* m_slot[NUM_SLOTS];
Card* m_aux;
Disk2CardManager m_disk2CardMgr;
LanguageCardManager m_languageCardMgr;
MockingboardCardManager m_mockingboardCardMgr;
class CMouseInterface* m_pMouseCard;
class CSuperSerialCard* m_pSSC;
class LanguageCardUnit* m_pLanguageCard;
class ParallelPrinterCard* m_pParallelPrinterCard;
class Z80Card* m_pZ80Card;
};

View File

@ -38,6 +38,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "SoundCore.h"
#include "SNESMAX.h"
#include "Interface.h"
#include "Harddisk.h"
CmdLine g_cmdLine;
std::string g_sConfigFile; // INI file to use instead of Registry
@ -156,6 +157,18 @@ bool ProcessCmdLine(LPSTR lpCmdLine)
lpNextArg = GetNextArg(lpNextArg);
g_cmdLine.szImageName_harddisk[SLOT7][HARDDISK_2] = lpCmdLine;
}
else if (strcmp(lpCmdLine, "-s0") == 0) // Language Card options for Apple II/II+
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
if (strcmp(lpCmdLine, "saturn") == 0 || strcmp(lpCmdLine, "saturn128") == 0)
g_cmdLine.uSaturnBanks = Saturn128K::kMaxSaturnBanks;
else if (strcmp(lpCmdLine, "saturn64") == 0)
g_cmdLine.uSaturnBanks = Saturn128K::kMaxSaturnBanks / 2;
else if (strcmp(lpCmdLine, "languagecard") == 0 || strcmp(lpCmdLine, "lc") == 0)
g_cmdLine.bSlot0LanguageCard = true;
}
else if (lpCmdLine[0] == '-' && lpCmdLine[1] == 's' && lpCmdLine[2] >= '1' && lpCmdLine[2] <= '7')
{
const UINT slot = lpCmdLine[2] - '0';
@ -175,6 +188,8 @@ bool ProcessCmdLine(LPSTR lpCmdLine)
}
if (strcmp(lpCmdLine, "hdc") == 0)
g_cmdLine.slotInsert[slot] = CT_GenericHDD;
if (strcmp(lpCmdLine, "saturn") == 0 || strcmp(lpCmdLine, "saturn128") == 0) // Support Saturn128 card in slot 1-7 too (GH#1279)
g_cmdLine.slotInsert[slot] = CT_Saturn128K;
if (strcmp(lpCmdLine, "megaaudio") == 0)
{
g_cmdLine.slotInsert[slot] = CT_MegaAudio;
@ -246,6 +261,21 @@ bool ProcessCmdLine(LPSTR lpCmdLine)
LogFileOutput("Unsupported arg: %s\n", lpCmdLine);
}
}
else if (strcmp(lpCmdLine, "-harddisknumblocks") == 0) // number of blocks to report for ProDOS
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
g_cmdLine.uHarddiskNumBlocks = atoi(lpCmdLine);
if (g_cmdLine.uHarddiskNumBlocks > kHarddiskMaxNumBlocks)
{
g_cmdLine.uHarddiskNumBlocks = kHarddiskMaxNumBlocks;
}
else
{
if (g_cmdLine.uHarddiskNumBlocks < 0)
g_cmdLine.uHarddiskNumBlocks = 0;
}
}
else if (strcmp(lpCmdLine, "-load-state") == 0)
{
lpCmdLine = GetCurrArg(lpNextArg);
@ -327,18 +357,6 @@ bool ProcessCmdLine(LPSTR lpCmdLine)
g_cmdLine.uRamWorksExPages = 1;
}
#endif
else if (strcmp(lpCmdLine, "-s0") == 0)
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
if (strcmp(lpCmdLine, "saturn") == 0 || strcmp(lpCmdLine, "saturn128") == 0)
g_cmdLine.uSaturnBanks = Saturn128K::kMaxSaturnBanks;
else if (strcmp(lpCmdLine, "saturn64") == 0)
g_cmdLine.uSaturnBanks = Saturn128K::kMaxSaturnBanks/2;
else if (strcmp(lpCmdLine, "languagecard") == 0 || strcmp(lpCmdLine, "lc") == 0)
g_cmdLine.bSlot0LanguageCard = true;
}
else if (strcmp(lpCmdLine, "-f8rom") == 0) // Use custom 2K ROM at [$F800..$FFFF]
{
lpCmdLine = GetCurrArg(lpNextArg);
@ -613,14 +631,16 @@ bool ProcessCmdLine(LPSTR lpCmdLine)
}
else if (strcmp(lpCmdLine, "-mb-audit") == 0) // enable selection of additional sound cards, eg. for mb-audit
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
g_cmdLine.supportExtraMBCardTypes = true;
}
else if (strcmp(lpCmdLine, "-no-disk2-stepper-defer") == 0) // a debug switch (likely to be removed in a future version)
else if (strcmp(lpCmdLine, "-no-disk2-stepper-defer") == 0) // a debug switch added at 1.30.11 / GH#1110 (likely to be removed in a future version)
{
g_cmdLine.noDisk2StepperDefer = true;
}
else if (strcmp(lpCmdLine, "-hdc-firmware-v1") == 0) // a debug switch added at 1.30.18 / GH#1277 (likely to be removed in a future version)
{
g_cmdLine.useHdcFirmwareV1 = true;
}
else // unsupported
{
LogFileOutput("Unsupported arg: %s\n", lpCmdLine);

View File

@ -34,8 +34,10 @@ struct CmdLine
enableDumpToRealPrinter = false;
supportExtraMBCardTypes = false;
noDisk2StepperDefer = false;
useHdcFirmwareV1 = false;
szSnapshotName = NULL;
szScreenshotFilename = NULL;
uHarddiskNumBlocks = 0;
uRamWorksExPages = 0;
uSaturnBanks = 0;
newVideoType = -1;
@ -77,11 +79,13 @@ struct CmdLine
bool enableDumpToRealPrinter;
bool supportExtraMBCardTypes;
bool noDisk2StepperDefer; // debug
bool useHdcFirmwareV1; // debug
SS_CARDTYPE slotInsert[NUM_SLOTS];
SlotInfo slotInfo[NUM_SLOTS];
LPCSTR szImageName_drive[NUM_SLOTS][NUM_DRIVES];
bool driveConnected[NUM_SLOTS][NUM_DRIVES];
LPCSTR szImageName_harddisk[NUM_SLOTS][NUM_HARDDISKS];
UINT uHarddiskNumBlocks;
LPSTR szSnapshotName;
LPSTR szScreenshotFilename;
UINT uRamWorksExPages;

View File

@ -50,7 +50,7 @@ const TCHAR CPageAdvanced::m_gameIOConnectorChoices[] =
"Cortechs Corp - CodeWriter\0" /* Protection key for Dynatech Microsoftware / Cortechs Corp "CodeWriter" */
"Robocom Ltd - Robo 500\0" /* Interface Module for Robocom Ltd's Robo 500 */
"Robocom Ltd - Robo 1000\0" /* Interface Module for Robocom Ltd's Robo 1000 */
"Robocom Ltd - Robo 1500\0"; /* Interface Module for Robocom Ltd's Robo 1500 */
"Robocom Ltd - Robo 1500, CAD-2P\0"; /* Interface Module for Robocom Ltd's Robo 1500, Robo Systems CAD-2P */
INT_PTR CALLBACK CPageAdvanced::DlgProc(HWND hWnd, UINT message, WPARAM wparam, LPARAM lparam)

View File

@ -131,20 +131,22 @@ int CopyProtectionDonglePDL(UINT pdl)
{
case DT_ROBOCOM500:
{
static BYTE robo500[8] = { 0x3F,0x2E,0x54,0x54,0x2E,0x22,0x72,0x17 }; // PDL3 lower bound
return robo500[roboComInterfaceModuleMode] + 1;
static BYTE robo500_lo[8] = { 0x3F,0x2E,0x54,0x54,0x2E,0x22,0x72,0x17 }; // PDL3 lower bound - see GH#1247
static BYTE robo500_hi[8] = { 0x6F,0x54,0x94,0x94,0x54,0x40,0xC4,0x2E }; // PDL3 upper bound - see GH#1247
// This mean value gives values that are very close to the actual 1000 & 1500 Module Interfaces - so assume it's similar for the 500 series.
return (robo500_lo[roboComInterfaceModuleMode] + robo500_hi[roboComInterfaceModuleMode] - 1) / 2;
}
case DT_ROBOCOM1000:
{
static BYTE robo1000[8] = { 0x17,0x72,0x22,0x2E,0x54,0x54,0x2E,0x3F }; // PDL3 lower bound
return robo1000[roboComInterfaceModuleMode] + 1;
static BYTE robo1000[8] = { 34,151,48,64,113,113,64,85 }; // Actual Module Interface values for PDL3
return robo1000[roboComInterfaceModuleMode];
}
case DT_ROBOCOM1500:
{
static BYTE robo1500[8] = { 0x72,0x17,0x2E,0x17,0x22,0x3F,0x54,0x22 }; // PDL3 lower bound
return robo1500[roboComInterfaceModuleMode] + 1;
static BYTE robo1500[8] = { 153,34,64,34,48,86,114,48 }; // Actual Module Interface values for PDL3
return robo1500[roboComInterfaceModuleMode];
}
default:

View File

@ -6408,6 +6408,9 @@ Update_t CmdOutputRun (int nArgs)
{
g_bScriptReadOk = true;
ConsolePrintFormat("%sRun script: ", CHC_INFO);
ConsolePrintFormat("%s%s", CHC_PATH, sFileName.c_str()); // Output pathname to console to indicate the script has been read
int nLine = script.GetNumLines();
Update_t bUpdateDisplay = UPDATE_NOTHING;
@ -6421,9 +6424,8 @@ Update_t CmdOutputRun (int nArgs)
}
else
{
std::string sMiniFileName = sFileName.substr(0, MIN(sFileName.size(), CONSOLE_WIDTH));
ConsolePrintFormat("%sCouldn't load filename:", CHC_ERROR);
ConsolePrintFormat("%s%s", CHC_STRING, sMiniFileName.c_str());
ConsolePrintFormat("%sCouldn't load script:", CHC_WARNING);
ConsolePrintFormat("%s%s", CHC_STRING, sFileName.c_str());
}
return ConsoleUpdate();
@ -9011,28 +9013,28 @@ void DebugInitialize ()
// Look in g_sCurrentDir, otherwise try g_sProgramDir
std::string pathname = g_sCurrentDir + debuggerAutoRunName;
if (pathname.size() >= MAX_PATH)
errno_t error = strncpy_s(g_aArgs[1].sArg, MAX_PATH, pathname.c_str(), pathname.size());
if (error != 0)
{
ConsolePrintFormat("%sPathname too long:", CHC_ERROR);
ConsolePrintFormat("%s%s", CHC_STRING, pathname.c_str());
}
else
{
strncpy_s(g_aArgs[1].sArg, MAX_PATH, pathname.c_str(), _TRUNCATE);
CmdOutputRun(1);
}
if (!g_bScriptReadOk)
{
pathname = g_sProgramDir + debuggerAutoRunName;
if (pathname.size() >= MAX_PATH)
error = strncpy_s(g_aArgs[1].sArg, MAX_PATH, pathname.c_str(), pathname.size());
if (error != 0)
{
ConsolePrintFormat("%sPathname too long:", CHC_ERROR);
ConsolePrintFormat("%s%s", CHC_STRING, pathname.c_str());
}
else
{
strncpy_s(g_aArgs[1].sArg, MAX_PATH, pathname.c_str(), _TRUNCATE);
CmdOutputRun(1);
}
}

View File

@ -2594,7 +2594,7 @@ void _DrawSoftSwitchLanguageCardBank( RECT & rect, const int iBankDisplay, int b
int iActiveBank = -1;
char cMemType = '?'; // Default to RAMWORKS
if (GetCurrentExpansionMemType() == CT_RamWorksIII) { cMemType = 'r'; iActiveBank = GetRamWorksActiveBank(); }
if (GetCurrentExpansionMemType() == CT_Saturn128K) { cMemType = 's'; iActiveBank = GetCardMgr().GetLanguageCard()->GetActiveBank(); }
if (GetCurrentExpansionMemType() == CT_Saturn128K) { cMemType = 's'; iActiveBank = GetCardMgr().GetLanguageCardMgr().GetLanguageCard()->GetActiveBank(); }
if (iActiveBank >= 0)
{

View File

@ -646,7 +646,6 @@ int ParseSymbolTable(const std::string & pPathFileName, SymbolTable_Index_e eSym
{
bFileDisplayed = true;
// TODO: Must check for buffer overflow !
ConsolePrintFormat( "%s%s"
, CHC_PATH
, pPathFileName.c_str()

View File

@ -1731,10 +1731,12 @@ ImageError_e CImageHelperBase::CheckZipFile(LPCTSTR pszImageFilename, ImageInfo*
SetImageInfo(pImageInfo, eFileZip, dwOffset, pImageType, dwSize);
pImageInfo2 = new ImageInfo(); // use this dummy one, as some members get overwritten during Detect()
pImageInfo2 = new ImageInfo(); // use this dummy one for remaining entries in zip archive, as some members get overwritten during Detect()
}
}
if (pImageInfo->pImageBuffer == pImageBuffer) // on error: avoid double-free when parent calls ImageClose()
pImageInfo->pImageBuffer = NULL;
delete [] pImageBuffer;
pImageBuffer = NULL;
}

View File

@ -53,6 +53,8 @@ Memory map (for slot 7):
C0F6 (r/w) LOW BYTE OF BLOCK NUMBER
C0F7 (r/w) HIGH BYTE OF BLOCK NUMBER
C0F8 (r) NEXT BYTE (legacy read-only port - still supported)
C0F9 (r) LOW BYTE OF DISK IMAGE SIZE IN BLOCKS
C0FA (r) HIGHT BYTE OF DISK IMAGE SIZE IN BLOCKS
Firmware notes:
. ROR ABS16,X and ROL ABS16,X - only used for $C081+s*$10 STATUS register:
@ -75,6 +77,8 @@ Overview
bytes, in a linear fashion. The internal formatting and meaning of each
block to be decided by the Apple's operating system (ProDOS). To create
an empty .HDV file, just create a 0 byte file.
Use the -harddisknumblocks n command line option to set the disk size
returned by ProDOS status calls.
2. Emulation code
There are 4 commands ProDOS will send to a block device.
@ -127,7 +131,7 @@ Overview
HarddiskInterfaceCard::HarddiskInterfaceCard(UINT slot) :
Card(CT_GenericHDD, slot)
Card(CT_GenericHDD, slot), m_userNumBlocks(0), m_useHdcFirmwareV1(false)
{
if (m_slot != SLOT5 && m_slot != SLOT7) // fixme
ThrowErrorInvalidSlot();
@ -162,7 +166,8 @@ void HarddiskInterfaceCard::InitializeIO(LPBYTE pCxRomPeripheral)
{
const DWORD HARDDISK_FW_SIZE = APPLE_SLOT_SIZE;
BYTE* pData = GetFrame().GetResource(IDR_HDDRVR_FW, "FIRMWARE", HARDDISK_FW_SIZE);
const WORD id = m_useHdcFirmwareV1 ? IDR_HDDRVR_FW : IDR_HDDRVR_V2_FW;
BYTE* pData = GetFrame().GetResource(id, "FIRMWARE", HARDDISK_FW_SIZE);
if (pData == NULL)
return;
@ -657,19 +662,31 @@ BYTE __stdcall HarddiskInterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BY
r = (BYTE)(pHDD->m_memblock & 0x00FF);
break;
case 0x5:
r = (BYTE)(pHDD->m_memblock & 0xFF00 >> 8);
r = (BYTE)((pHDD->m_memblock & 0xFF00) >> 8);
break;
case 0x6:
r = (BYTE)(pHDD->m_diskblock & 0x00FF);
break;
case 0x7:
r = (BYTE)(pHDD->m_diskblock & 0xFF00 >> 8);
r = (BYTE)((pHDD->m_diskblock & 0xFF00) >> 8);
break;
case 0x8: // Legacy: continue to support this I/O port for old HDD firmware
r = pHDD->m_buf[pHDD->m_buf_ptr];
if (pHDD->m_buf_ptr < sizeof(pHDD->m_buf)-1)
pHDD->m_buf_ptr++;
break;
case 0x9:
if (pHDD->m_imageloaded)
r = (BYTE)(pCard->GetImageSizeInBlocks(pHDD->m_imagehandle) & 0x00ff);
else
r = 0;
break;
case 0xa:
if (pHDD->m_imageloaded)
r = (BYTE)((pCard->GetImageSizeInBlocks(pHDD->m_imagehandle) & 0xff00) >> 8);
else
r = 0;
break;
default:
pHDD->m_status_next = DISK_STATUS_OFF;
r = IO_Null(pc, addr, bWrite, d, nExecutedCycles);
@ -694,7 +711,9 @@ BYTE __stdcall HarddiskInterfaceCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, B
case 0x0: // r/o: status
case 0x1: // r/o: execute
case 0x8: // r/o: legacy next-data port
// Writing to these 3 read-only registers is a no-op.
case 0x9: // r/o: low byte of image size
case 0xa: // r/o: high byte of image size
// Writing to these 5 read-only registers is a no-op.
// NB. Don't change m_status_next, as UpdateLightStatus() has a huge performance cost!
// Firmware has a busy-wait loop doing "rol hd_status,x"
// - this RMW opcode does an IORead() then an IOWrite(), and the loop iterates ~100 times!
@ -729,6 +748,16 @@ BYTE __stdcall HarddiskInterfaceCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, B
return r;
}
UINT HarddiskInterfaceCard::GetImageSizeInBlocks(ImageInfo* const pImageInfo)
{
if (m_userNumBlocks != 0)
return m_userNumBlocks;
UINT numberOfBlocks = (pImageInfo ? pImageInfo->uImageSize : 0) / HD_BLOCK_SIZE;
if (numberOfBlocks > kHarddiskMaxNumBlocks)
numberOfBlocks = kHarddiskMaxNumBlocks;
return numberOfBlocks;
}
//===========================================================================
void HarddiskInterfaceCard::UpdateLightStatus(HardDiskDrive* pHDD)
@ -766,7 +795,8 @@ bool HarddiskInterfaceCard::ImageSwap(void)
// 2: Updated $C7nn firmware to fix GH#319
// 3: Updated $Csnn firmware to fix GH#996 (now slot-independent code)
// Added: Not Busy Cycle
static const UINT kUNIT_VERSION = 3;
// 4: Updated $Csnn firmware to fix GH#1264
static const UINT kUNIT_VERSION = 4;
#define SS_YAML_VALUE_CARD_HDD "Generic HDD"

View File

@ -34,6 +34,8 @@ enum HardDrive_e
NUM_HARDDISKS
};
const UINT kHarddiskMaxNumBlocks = 0xffff; // Maximum number of blocks we can report.
class HardDiskDrive
{
public:
@ -98,6 +100,8 @@ public:
void Unplug(const int iDrive);
bool IsDriveUnplugged(const int iDrive);
void LoadLastDiskImage(const int iDrive);
void SetUserNumBlocks(UINT numBlocks) { m_userNumBlocks = numBlocks; }
void UseHdcFirmwareV1(void) { m_useHdcFirmwareV1 = true; }
void GetLightStatus(Disk_Status_e* pDisk1Status);
bool ImageSwap(void);
@ -117,7 +121,7 @@ private:
const std::string& DiskGetBaseName(const int iDrive);
bool SelectImage(const int drive, LPCSTR pszFilename);
void UpdateLightStatus(HardDiskDrive* pHDD);
UINT GetImageSizeInBlocks(ImageInfo* const pImageInfo);
void SaveSnapshotHDDUnit(YamlSaveHelper& yamlSaveHelper, UINT unit);
bool LoadSnapshotHDDUnit(YamlLoadHelper& yamlLoadHelper, UINT unit);
@ -126,6 +130,8 @@ private:
BYTE m_unitNum; // b7=unit
BYTE m_command;
UINT64 m_notBusyCycle;
UINT m_userNumBlocks;
bool m_useHdcFirmwareV1;
bool m_saveDiskImage; // Save the DiskImage name to Registry

View File

@ -333,6 +333,22 @@ static char ClipboardCurrChar(bool bIncPtr)
return nKey;
}
static BYTE ClipboardReadOrPeek(bool incPtr)
{
if (g_bPasteFromClipboard)
ClipboardInit();
if (g_bClipboardActive)
{
if (*lptstr == 0)
ClipboardDone();
else
return 0x80 | ClipboardCurrChar(incPtr);
}
return 0;
}
//===========================================================================
const UINT kAKDNumElements = 256/64;
@ -402,43 +418,29 @@ BYTE KeybReadData (void)
{
LogFileTimeUntilFirstKeyRead();
if (g_bPasteFromClipboard)
ClipboardInit();
if (g_bClipboardActive)
{
if(*lptstr == 0)
ClipboardDone();
else
return 0x80 | ClipboardCurrChar(false);
}
//
BYTE res = ClipboardReadOrPeek(false);
if (res)
return res;
return keycode | (keywaiting ? 0x80 : 0);
}
//===========================================================================
BYTE KeybReadFlag (void)
BYTE KeybClearStrobe(void)
{
if (g_bPasteFromClipboard)
ClipboardInit();
if (g_bClipboardActive)
{
if(*lptstr == 0)
ClipboardDone();
else
return 0x80 | ClipboardCurrChar(true);
}
//
keywaiting = 0;
if (IS_APPLE2) // Include Pravets machines too?
return keycode;
return ClipboardReadOrPeek(true);
}
BYTE KeybReadFlag (void)
{
_ASSERT(!IS_APPLE2); // And also not Pravets machines?
BYTE res = KeybClearStrobe();
if (res)
return res;
// AKD

View File

@ -17,6 +17,7 @@ void KeybQueueKeypress(WPARAM key, Keystroke_e bASCII);
void KeybToggleCapsLock ();
void KeybToggleP8ACapsLock ();
void KeybAnyKeyDown(UINT message, WPARAM wparam, bool bIsExtended);
BYTE KeybClearStrobe(void);
BYTE KeybReadData (void);
BYTE KeybReadFlag (void);
void KeybSaveSnapshot(class YamlSaveHelper& yamlSaveHelper);

View File

@ -24,11 +24,32 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/* Description: Language Card and Saturn 128K emulation
*
* Author: various
*
* Note: Here's what isn't fully emulated:
* From UTAII 5-42 (Application Note: Multiple RAM Card Configurations)
* . For II/II, INHIBIT' (disable motherboard ROM for $D000-$FFFF) and Apple's 16K RAM card isn't correct:
* . "If the expansion RAM is not enabled on a RAM card, the ROM on the card will respond to $F800-$FFFF addressing - period."
* . In UTAIIe 5-24, Sather describes this as "a particularly nettlesome associated fact"!
* . NB. "When INHIBIT' is low on the Apple IIe, all motherboard ROM is disabled, including high RAM."
* . Note: I assume a Saturn card "will release the $F800-$FFFF range when RAM on the card is disabled", since there's no F8 ROM on the Saturn.
* . Summary: for a II/II+ with an *Apple* 16K RAM card in slot 0, when (High) RAM is disabled, then:
* . ROM on the slot 0 card will respond, along with any Saturn card(s) in other slots which pull INHIBIT' low.
* . *** AppleWin emulates a slot 0 LC as if the Sather h/w mod had been applied.
* . [UTAII 5-42] "Enable two RAM cards for writing simultaneously..."
* "both RAM cards will accept the data from a single store instruction to the $D000-$FFFF range"
* *** AppleWin only stores to the last accessed RAM card.
* . Presumably enabling two RAM cards for reading RAM will both respond and the result is the OR-sum?
* *** AppleWin only loads from the last accessed RAM card.
* . The 16K RAM card has a socket for an F8 ROM, whereas the Saturn card doesn't.
* Also see UTAII 6-6, where Firmware card and 16K RAM card are described.
* . Sather refers to the Apple 16K RAM card, which is just the Apple Language Card.
*
*/
#include "StdAfx.h"
#include "LanguageCard.h"
#include "CardManager.h"
#include "Core.h"
#include "CPU.h" // GH#700
#include "Log.h"
@ -45,17 +66,30 @@ LanguageCardUnit * LanguageCardUnit::create(UINT slot)
LanguageCardUnit::LanguageCardUnit(SS_CARDTYPE type, UINT slot) :
Card(type, slot),
m_uLastRamWrite(0)
m_uLastRamWrite(0),
m_memMode(kMemModeInitialState),
m_pMemory(NULL)
{
if (m_slot != LanguageCardUnit::kSlot0)
if (type != CT_Saturn128K && m_slot != LanguageCardUnit::kSlot0)
ThrowErrorInvalidSlot();
SetMemMainLanguageCard(NULL, true);
if (m_slot == SLOT0)
SetMemMainLanguageCard(NULL, SLOT0, true);
}
LanguageCardUnit::~LanguageCardUnit(void)
{
SetMemMainLanguageCard(NULL);
// Nothing to do for SetMemMainLanguageCard():
// . if //e, then no ptr to clean up (since just using memmain)
// . else: subclass will do ptr clean up
}
void LanguageCardUnit::Reset(const bool powerCycle)
{
// For power on: card's ctor will have set card's local memmode to LanguageCardUnit::kMemModeInitialState.
// For reset: II/II+ unaffected, so only for //e or above.
if (IsAppleIIeOrAbove(GetApple2Type()))
SetLCMemMode(LanguageCardUnit::kMemModeInitialState);
}
void LanguageCardUnit::InitializeIO(LPBYTE pCxRomPeripheral)
@ -67,9 +101,10 @@ BYTE __stdcall LanguageCardUnit::IO(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValu
{
UINT uSlot = ((uAddr & 0xff) >> 4) - 8;
LanguageCardUnit* pLC = (LanguageCardUnit*) MemGetSlotParameters(uSlot);
_ASSERT(uSlot == SLOT0);
DWORD memmode = GetMemMode();
DWORD lastmemmode = memmode;
UINT memmode = pLC->GetLCMemMode();
UINT lastmemmode = memmode;
memmode &= ~(MF_BANK2 | MF_HIGHRAM);
if (!(uAddr & 8))
@ -95,7 +130,16 @@ BYTE __stdcall LanguageCardUnit::IO(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValu
}
pLC->SetLastRamWrite( ((uAddr & 1) && !bWrite) ); // UTAIIe:5-23
SetMemMode(memmode);
pLC->SetLCMemMode(memmode);
const bool bCardChanged = GetCardMgr().GetLanguageCardMgr().GetLastSlotToSetMainMemLC() != SLOT0;
if (bCardChanged)
{
if (pLC->QueryType() == CT_LanguageCardIIe)
SetMemMainLanguageCard(NULL, SLOT0, true);
else // CT_LanguageCard
SetMemMainLanguageCard(pLC->m_pMemory, SLOT0);
}
//
@ -104,8 +148,10 @@ BYTE __stdcall LanguageCardUnit::IO(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValu
// IF THE MEMORY PAGING MODE HAS CHANGED, UPDATE OUR MEMORY IMAGES AND
// WRITE TABLES.
if (lastmemmode != memmode)
if ((lastmemmode != memmode) || bCardChanged)
{
// NB. Always SetMemMode() - locally may be same, but card may've changed
SetMemMode((GetMemMode() & ~MF_LANGCARD_MASK) | (memmode & MF_LANGCARD_MASK));
MemUpdatePaging(0); // Initialize=0
}
@ -143,6 +189,11 @@ bool LanguageCardUnit::IsOpcodeRMWabs(WORD addr)
return false;
}
void LanguageCardUnit::SetGlobalLCMemMode(void)
{
SetMemMode((GetMemMode() & ~MF_LANGCARD_MASK) | (GetLCMemMode() & MF_LANGCARD_MASK));
}
//-------------------------------------
LanguageCardSlot0 * LanguageCardSlot0::create(UINT slot)
@ -154,13 +205,16 @@ LanguageCardSlot0::LanguageCardSlot0(SS_CARDTYPE type, UINT slot)
: LanguageCardUnit(type, slot)
{
m_pMemory = new BYTE[kMemBankSize];
SetMemMainLanguageCard(m_pMemory);
if (m_slot == SLOT0)
SetMemMainLanguageCard(m_pMemory, SLOT0);
}
LanguageCardSlot0::~LanguageCardSlot0(void)
{
delete [] m_pMemory;
m_pMemory = NULL;
if (m_slot == SLOT0)
SetMemMainLanguageCard(NULL, SLOT0);
}
//
@ -186,15 +240,15 @@ const std::string& LanguageCardSlot0::GetSnapshotCardName(void)
void LanguageCardSlot0::SaveLCState(YamlSaveHelper& yamlSaveHelper)
{
yamlSaveHelper.SaveHexUint32(SS_YAML_KEY_MEMORYMODE, GetMemMode() & (MF_WRITERAM|MF_HIGHRAM|MF_BANK2));
yamlSaveHelper.SaveHexUint32(SS_YAML_KEY_MEMORYMODE, GetLCMemMode() & MF_LANGCARD_MASK);
yamlSaveHelper.SaveUint(SS_YAML_KEY_LASTRAMWRITE, GetLastRamWrite() ? 1 : 0);
}
void LanguageCardSlot0::LoadLCState(YamlLoadHelper& yamlLoadHelper)
{
DWORD memMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_MEMORYMODE) & MF_LANGCARD_MASK;
UINT memMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_MEMORYMODE) & MF_LANGCARD_MASK;
BOOL lastRamWrite = yamlLoadHelper.LoadUint(SS_YAML_KEY_LASTRAMWRITE) ? TRUE : FALSE;
SetMemMode( (GetMemMode() & ~MF_LANGCARD_MASK) | memMode );
SetLCMemMode(memMode);
SetLastRamWrite(lastRamWrite);
}
@ -261,7 +315,8 @@ Saturn128K::Saturn128K(UINT slot, UINT banks)
for (UINT i = 1; i < m_uSaturnTotalBanks; i++)
m_aSaturnBanks[i] = new BYTE[kMemBankSize]; // Saturn banks are 16K, max 8 banks/card
SetMemMainLanguageCard( m_aSaturnBanks[ m_uSaturnActiveBank ] );
if (slot == SLOT0)
::SetMemMainLanguageCard(m_aSaturnBanks[m_uSaturnActiveBank], SLOT0);
}
Saturn128K::~Saturn128K(void)
@ -276,6 +331,9 @@ Saturn128K::~Saturn128K(void)
m_aSaturnBanks[i] = NULL;
}
}
// NB. want the Saturn128K object that set the ptr via ::SetMemMainLanguageCard() to now set it to NULL (may be from SLOT0 or another slot)
// In reality, dtor only called when whole VM is being destroyed, so won't have have use-after-frees.
}
UINT Saturn128K::GetActiveBank(void)
@ -317,7 +375,8 @@ BYTE __stdcall Saturn128K::IO(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValue, ULO
return bWrite ? 0 : MemReadFloatingBus(nExecutedCycles);
bool bBankChanged = false;
DWORD memmode=0, lastmemmode=0;
UINT memmode = pLC->GetLCMemMode();
UINT lastmemmode = memmode;
if (uAddr & (1<<2))
{
@ -334,13 +393,11 @@ BYTE __stdcall Saturn128K::IO(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValue, ULO
pLC->m_uSaturnActiveBank = pLC->m_uSaturnTotalBanks-1; // FIXME: just prevent crash for now!
}
SetMemMainLanguageCard( pLC->m_aSaturnBanks[ pLC->m_uSaturnActiveBank ] );
::SetMemMainLanguageCard(pLC->m_aSaturnBanks[pLC->m_uSaturnActiveBank], uSlot);
bBankChanged = true;
}
else
{
memmode = GetMemMode();
lastmemmode = memmode;
memmode &= ~(MF_BANK2 | MF_HIGHRAM);
if (!(uAddr & 8))
@ -355,15 +412,24 @@ BYTE __stdcall Saturn128K::IO(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValue, ULO
memmode &= ~MF_WRITERAM;
pLC->SetLastRamWrite(uAddr & 1); // Saturn differs from Apple's 16K LC: any access (LC is read-only)
SetMemMode(memmode);
pLC->SetLCMemMode(memmode);
bBankChanged = GetCardMgr().GetLanguageCardMgr().GetLastSlotToSetMainMemLC() != uSlot;
if (bBankChanged)
{
::SetMemMainLanguageCard(pLC->m_aSaturnBanks[pLC->m_uSaturnActiveBank], uSlot);
}
}
// NB. Unlike LC, no need to check if next opcode is STA $C002-5, as Saturn is not for //e
// NB. Saturn can be put in any slot but MemOptimizeForModeChanging() currently only supports LC in slot 0.
// . This optimization (check if next opcode is STA $C002-5) isn't essential, so skip it for now.
// IF THE MEMORY PAGING MODE HAS CHANGED, UPDATE OUR MEMORY IMAGES AND
// WRITE TABLES.
if ((lastmemmode != memmode) || bBankChanged)
{
// NB. Always SetMemMode() - locally may be same, but card or bank may've changed
SetMemMode((GetMemMode() & ~MF_LANGCARD_MASK) | (memmode & MF_LANGCARD_MASK));
MemUpdatePaging(0); // Initialize=0
}
@ -393,13 +459,6 @@ const std::string& Saturn128K::GetSnapshotCardName(void)
void Saturn128K::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
{
if (!IsApple2PlusOrClone(GetApple2Type()))
{
_ASSERT(0);
LogFileOutput("Warning: Save-state attempted to save %s for //e or above\n", GetSnapshotCardName().c_str());
return; // No Saturn support for //e and above
}
YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardName(), m_slot, kUNIT_SATURN_VER);
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
@ -453,13 +512,18 @@ bool Saturn128K::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
yamlLoadHelper.PopMap();
}
SetMemMainLanguageCard( m_aSaturnBanks[ m_uSaturnActiveBank ] );
// NB. MemUpdatePaging(TRUE) called at end of Snapshot_LoadState_v2()
// NB. MemInitializeFromSnapshot() called at end of Snapshot_LoadState_v2():
// . SetMemMainLanguageCard() for the slot/card that last set the 16KB LC bank
// . MemUpdatePaging(TRUE)
return true;
}
void Saturn128K::SetMemMainLanguageCard(void)
{
::SetMemMainLanguageCard(m_aSaturnBanks[m_uSaturnActiveBank], m_slot);
}
void Saturn128K::SetSaturnMemorySize(UINT banks)
{
g_uSaturnBanksFromCmdLine = banks;
@ -469,3 +533,71 @@ UINT Saturn128K::GetSaturnMemorySize()
{
return g_uSaturnBanksFromCmdLine;
}
//-------------------------------------
/*
* LangauageCardManager:
* . manage reset for all cards (eg. II/II+'s LC is unaffected, whereas //e's LC is)
* . manage lastSlotToSetMainMemLC
* . TODO: assist with debugger's display of "sNN" for active 16K bank
*/
void LanguageCardManager::Reset(const bool powerCycle /*=false*/)
{
if (IsApple2PlusOrClone(GetApple2Type()) && !powerCycle) // For reset : II/II+ unaffected
return;
if (GetLanguageCard())
GetLanguageCard()->SetLastRamWrite(0);
if (IsApple2PlusOrClone(GetApple2Type()) && GetCardMgr().QuerySlot(SLOT0) == CT_Empty)
SetMemMode(0);
else
SetMemMode(LanguageCardUnit::kMemModeInitialState);
}
void LanguageCardManager::SetMemModeFromSnapshot(void)
{
// If multiple "Language Cards" (eg. LC+Saturn or 2xSaturn) then setup via the last card that selected the 16KB LC bank.
// NB. Skip if not Saturn card (ie. a LC), since LC's are only in slot0 and in the ctor it has called SetMainMemLanguageCard()
if (GetCardMgr().QuerySlot(m_lastSlotToSetMainMemLCFromSnapshot) == CT_Saturn128K)
{
Saturn128K& saturn = dynamic_cast<Saturn128K&>(GetCardMgr().GetRef(m_lastSlotToSetMainMemLCFromSnapshot));
saturn.SetMemMainLanguageCard();
}
if (GetCardMgr().QuerySlot(m_lastSlotToSetMainMemLCFromSnapshot) != CT_Empty)
dynamic_cast<LanguageCardUnit&>(GetCardMgr().GetRef(m_lastSlotToSetMainMemLCFromSnapshot)).SetGlobalLCMemMode();
}
bool LanguageCardManager::SetLanguageCard(SS_CARDTYPE type)
{
if (type == CT_Empty)
{
m_pLanguageCard = NULL;
return true;
}
_ASSERT(GetLanguageCard() == NULL);
if (GetLanguageCard())
return false; // Only support one language card
switch (type)
{
case CT_LanguageCard:
m_pLanguageCard = LanguageCardSlot0::create(SLOT0);
break;
case CT_LanguageCardIIe:
m_pLanguageCard = LanguageCardUnit::create(SLOT0);
break;
case CT_Saturn128K:
m_pLanguageCard = new Saturn128K(SLOT0, Saturn128K::GetSaturnMemorySize());
break;
default:
_ASSERT(0);
return false;
}
return true;
}

View File

@ -15,10 +15,9 @@ public:
virtual ~LanguageCardUnit(void);
virtual void Destroy(void) {}
virtual void Reset(const bool powerCycle) {}
virtual void Reset(const bool powerCycle);
virtual void Update(const ULONG nExecutedCycles) {}
virtual void InitializeIO(LPBYTE pCxRomPeripheral);
virtual UINT GetActiveBank(void) { return 0; } // Always 0 as only 1x 16K bank
virtual void SaveSnapshot(YamlSaveHelper& yamlSaveHelper) { } // A no-op for //e - called from CardManager::SaveSnapshot()
@ -26,6 +25,9 @@ public:
BOOL GetLastRamWrite(void) { return m_uLastRamWrite; }
void SetLastRamWrite(BOOL count) { m_uLastRamWrite = count; }
UINT GetLCMemMode(void) { return m_memMode; }
void SetLCMemMode(UINT memMode) { m_memMode = memMode; }
void SetGlobalLCMemMode(void);
SS_CARDTYPE GetMemoryType(void) { return QueryType(); }
bool IsOpcodeRMWabs(WORD addr);
@ -37,8 +39,11 @@ public:
protected:
LanguageCardUnit(SS_CARDTYPE type, UINT slot);
LPBYTE m_pMemory;
private:
UINT m_uLastRamWrite;
UINT m_memMode;
};
//
@ -64,8 +69,6 @@ protected:
void SaveLCState(class YamlSaveHelper& yamlSaveHelper);
void LoadLCState(class YamlLoadHelper& yamlLoadHelper);
LPBYTE m_pMemory;
private:
const std::string& GetSnapshotMemStructName(void);
};
@ -85,6 +88,8 @@ public:
virtual void SaveSnapshot(YamlSaveHelper& yamlSaveHelper);
virtual bool LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version);
void SetMemMainLanguageCard(void);
static UINT GetSaturnMemorySize();
static void SetSaturnMemorySize(UINT banks);
@ -103,3 +108,34 @@ private:
UINT m_uSaturnActiveBank; // Saturn 128K Language Card Bank 0 .. 7
LPBYTE m_aSaturnBanks[kMaxSaturnBanks];
};
//
// Language Card manager
//
class LanguageCardManager
{
public:
LanguageCardManager(void) :
m_pLanguageCard(NULL),
m_lastSlotToSetMainMemLC(SLOT0),
m_lastSlotToSetMainMemLCFromSnapshot(SLOT0)
{}
~LanguageCardManager(void) {}
void Reset(const bool powerCycle = false);
UINT GetLastSlotToSetMainMemLC(void) { return m_lastSlotToSetMainMemLC; }
void SetLastSlotToSetMainMemLC(UINT slot) { m_lastSlotToSetMainMemLC = slot; }
void SetLastSlotToSetMainMemLCFromSnapshot(UINT slot) { m_lastSlotToSetMainMemLCFromSnapshot = slot; }
LanguageCardUnit* GetLanguageCard(void) { return m_pLanguageCard; }
bool SetLanguageCard(SS_CARDTYPE type);
void SetMemModeFromSnapshot(void);
private:
LanguageCardUnit* m_pLanguageCard;
UINT m_lastSlotToSetMainMemLC;
UINT m_lastSlotToSetMainMemLCFromSnapshot;
};

View File

@ -56,9 +56,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "YamlHelper.h"
// In this file allocate the 64KB of RAM with aligned memory allocations (0x10000)
// to ease mapping between Apple ][ and host memory space (while debugging).
// this is not available in Visual Studio
// to ease mapping between Apple ][ and host memory space (while debugging) & also to fix GH#1285.
// This is not available in Windows CRT:
// https://en.cppreference.com/w/c/memory/aligned_alloc
#ifdef _MSC_VER
@ -66,6 +65,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#define ALIGNED_ALLOC(size) (LPBYTE)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE)
#define ALIGNED_FREE(ptr) VirtualFree(ptr, 0, MEM_RELEASE)
#else
#include <unistd.h>
#include <sys/mman.h>
// use plain "new" in gcc (where debugging needs are less important)
#define ALIGNED_ALLOC(size) new BYTE[size]
#define ALIGNED_FREE(ptr) delete [] ptr
@ -76,20 +77,20 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// . Sather uses INTCXROM instead of SLOTCXROM' (used by the Apple//e Tech Ref Manual), so keep to this
// convention too since UTAIIe is the reference for most of the logic that we implement in the emulator.
#define SW_80STORE (memmode & MF_80STORE)
#define SW_ALTZP (memmode & MF_ALTZP)
#define SW_AUXREAD (memmode & MF_AUXREAD)
#define SW_AUXWRITE (memmode & MF_AUXWRITE)
#define SW_BANK2 (memmode & MF_BANK2)
#define SW_HIGHRAM (memmode & MF_HIGHRAM)
#define SW_HIRES (memmode & MF_HIRES)
#define SW_PAGE2 (memmode & MF_PAGE2)
#define SW_SLOTC3ROM (memmode & MF_SLOTC3ROM)
#define SW_INTCXROM (memmode & MF_INTCXROM)
#define SW_WRITERAM (memmode & MF_WRITERAM)
#define SW_IOUDIS (memmode & MF_IOUDIS)
#define SW_ALTROM0 (memmode & MF_ALTROM0) // For Copam Base64A
#define SW_ALTROM1 (memmode & MF_ALTROM1) // For Copam Base64A
#define SW_80STORE (g_memmode & MF_80STORE)
#define SW_ALTZP (g_memmode & MF_ALTZP)
#define SW_AUXREAD (g_memmode & MF_AUXREAD)
#define SW_AUXWRITE (g_memmode & MF_AUXWRITE)
#define SW_BANK2 (g_memmode & MF_BANK2)
#define SW_HIGHRAM (g_memmode & MF_HIGHRAM)
#define SW_HIRES (g_memmode & MF_HIRES)
#define SW_PAGE2 (g_memmode & MF_PAGE2)
#define SW_SLOTC3ROM (g_memmode & MF_SLOTC3ROM)
#define SW_INTCXROM (g_memmode & MF_INTCXROM)
#define SW_WRITERAM (g_memmode & MF_WRITERAM)
#define SW_IOUDIS (g_memmode & MF_IOUDIS)
#define SW_ALTROM0 (g_memmode & MF_ALTROM0) // For Copam Base64A
#define SW_ALTROM1 (g_memmode & MF_ALTROM1) // For Copam Base64A
/*
MEMORY MANAGEMENT SOFT SWITCHES
@ -224,7 +225,7 @@ static LPBYTE pCxRomPeripheral = NULL;
static LPBYTE g_pMemMainLanguageCard = NULL;
static DWORD memmode = LanguageCardUnit::kMemModeInitialState;
static DWORD g_memmode = LanguageCardUnit::kMemModeInitialState;
static BOOL modechanging = 0; // An Optimisation: means delay calling UpdatePaging() for 1 instruction
static UINT memrompages = 1;
@ -242,7 +243,15 @@ static LPBYTE RWpages[kMaxExMemoryBanks]; // pointers to RW memory banks
static const UINT kNumAnnunciators = 4;
static bool g_Annunciator[kNumAnnunciators] = {};
static const UINT num64KPages = 2; // number of 64K pages used to create hardware circular buffer
#ifdef _MSC_VER
static HANDLE g_hMemImage = NULL; // NB. When not initialised, this handle is NULL (not INVALID_HANDLE_VALUE)
#else
static FILE * g_hMemTempFile = NULL;
#endif
BYTE __stdcall IO_Annunciator(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles);
static void FreeMemImage(void);
//=============================================================================
@ -371,25 +380,27 @@ UINT GetRamWorksActiveBank(void)
static BOOL GetLastRamWrite(void)
{
if (GetCardMgr().GetLanguageCard())
return GetCardMgr().GetLanguageCard()->GetLastRamWrite();
if (GetCardMgr().GetLanguageCardMgr().GetLanguageCard())
return GetCardMgr().GetLanguageCardMgr().GetLanguageCard()->GetLastRamWrite();
return 0;
}
static void SetLastRamWrite(BOOL count)
{
if (GetCardMgr().GetLanguageCard())
GetCardMgr().GetLanguageCard()->SetLastRamWrite(count);
if (GetCardMgr().GetLanguageCardMgr().GetLanguageCard())
GetCardMgr().GetLanguageCardMgr().GetLanguageCard()->SetLastRamWrite(count);
}
//
void SetMemMainLanguageCard(LPBYTE ptr, bool bMemMain /*=false*/)
void SetMemMainLanguageCard(LPBYTE ptr, UINT slot, bool bMemMain /*=false*/)
{
if (bMemMain)
g_pMemMainLanguageCard = memmain+0xC000;
else
g_pMemMainLanguageCard = ptr;
GetCardMgr().GetLanguageCardMgr().SetLastSlotToSetMainMemLC(slot);
}
LPBYTE GetCxRomPeripheral(void)
@ -417,7 +428,10 @@ static BYTE __stdcall IOWrite_C00x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULON
static BYTE __stdcall IORead_C01x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles)
{
if (IS_APPLE2) // Include Pravets machines too?
return KeybReadFlag();
{
KeybClearStrobe();
return IO_Null(pc, addr, bWrite, d, nExecutedCycles); // GH#1261
}
bool res = false;
switch (addr & 0xf)
@ -445,7 +459,7 @@ static BYTE __stdcall IORead_C01x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG
static BYTE __stdcall IOWrite_C01x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles)
{
return KeybReadFlag();
return KeybClearStrobe();
}
//-------------------------------------
@ -643,12 +657,12 @@ static BYTE __stdcall IOWrite_C07x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULON
case 0xC: return IO_Null(pc, addr, bWrite, d, nExecutedCycles);
case 0xD: return IO_Null(pc, addr, bWrite, d, nExecutedCycles);
case 0xE: if (IS_APPLE2C())
SetMemMode(memmode | MF_IOUDIS); // On: disable IOU access for addresses $C058 to $C05F; enable access to DHIRES switch
SetMemMode(g_memmode | MF_IOUDIS); // On: disable IOU access for addresses $C058 to $C05F; enable access to DHIRES switch
else
return IO_Null(pc, addr, bWrite, d, nExecutedCycles);
break;
case 0xF: if (IS_APPLE2C())
SetMemMode(memmode & ~MF_IOUDIS); // Off: enable IOU access for addresses $C058 to $C05F; disable access to DHIRES switch
SetMemMode(g_memmode & ~MF_IOUDIS); // Off: enable IOU access for addresses $C058 to $C05F; disable access to DHIRES switch
else
return IO_Null(pc, addr, bWrite, d, nExecutedCycles);
break;
@ -1073,14 +1087,14 @@ static bool IsCardInSlot(UINT slot)
DWORD GetMemMode(void)
{
return memmode;
return g_memmode;
}
void SetMemMode(DWORD uNewMemMode)
{
#if defined(_DEBUG) && 0
static DWORD dwOldDiff = 0;
DWORD dwDiff = memmode ^ uNewMemMode;
DWORD dwDiff = g_memmode ^ uNewMemMode;
dwDiff &= ~(MF_SLOTC3ROM | MF_INTCXROM);
if (dwOldDiff != dwDiff)
{
@ -1115,7 +1129,7 @@ void SetMemMode(DWORD uNewMemMode)
OutputDebugString(str.c_str());
}
#endif
memmode = uNewMemMode;
g_memmode = uNewMemMode;
}
//===========================================================================
@ -1127,18 +1141,15 @@ static void UpdatePaging(BOOL initialize);
// . CtrlReset() Soft-reset (Ctrl+Reset) for //e
void MemResetPaging()
{
ResetPaging(0); // Initialize=0
ResetPaging(FALSE); // Initialize=0
}
// Call by:
// . MemResetPaging() -> ResetPaging(FALSE)
// . MemReset() -> ResetPaging(TRUE)
static void ResetPaging(BOOL initialize)
{
SetLastRamWrite(0);
if (IsApple2PlusOrClone(GetApple2Type()) && GetCardMgr().QuerySlot(SLOT0) == CT_Empty)
SetMemMode(0);
else
SetMemMode(LanguageCardUnit::kMemModeInitialState);
GetCardMgr().GetLanguageCardMgr().Reset(initialize);
UpdatePaging(initialize);
}
@ -1287,7 +1298,7 @@ void MemDestroy()
{
ALIGNED_FREE(memaux);
ALIGNED_FREE(memmain);
ALIGNED_FREE(memimage);
FreeMemImage();
delete [] memdirty;
delete [] memrom;
@ -1507,12 +1518,153 @@ bool MemIsAddrCodeMemory(const USHORT addr)
//===========================================================================
static void FreeMemImage(void)
{
#ifdef _MSC_VER
if (g_hMemImage)
{
for (UINT i = 0; i < num64KPages; i++)
UnmapViewOfFile(memimage + i * _6502_MEM_LEN);
CloseHandle(g_hMemImage);
g_hMemImage = NULL;
}
else
{
ALIGNED_FREE(memimage);
}
#else
if (g_hMemTempFile)
{
// unmap the whole region, everything inside will be unmapped too
munmap(memimage, num64KPages * _6502_MEM_LEN);
fclose(g_hMemTempFile);
g_hMemTempFile = NULL;
}
else
{
ALIGNED_FREE(memimage);
}
#endif
}
static LPBYTE AllocMemImage(void)
{
#ifdef _MSC_VER
LPBYTE baseAddr = NULL;
// Allocate memory for 'memimage' (and the alias 'mem')
// . Setup so we have 2 consecutive virtual 64K regions pointing to the same physical 64K region.
// . This is a fix (and optimisation) for 6502 opcodes that do a 16-bit read at 6502 address $FFFF. (GH#1285)
SYSTEM_INFO info;
GetSystemInfo(&info);
bool res = (info.dwAllocationGranularity == _6502_MEM_LEN);
if (res)
{
UINT retry = 10;
do
{
res = false;
const SIZE_T totalVirtualSize = _6502_MEM_LEN * num64KPages;
baseAddr = (LPBYTE)VirtualAlloc(0, totalVirtualSize, MEM_RESERVE, PAGE_NOACCESS);
if (baseAddr == NULL)
break;
VirtualFree(baseAddr, 0, MEM_RELEASE);
// Create a file mapping object of [64K] size that is backed by the system paging file.
g_hMemImage = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, _6502_MEM_LEN, NULL);
// NB. Returns NULL on failure (not INVALID_HANDLE_VALUE)
if (g_hMemImage == NULL)
break;
UINT count = 0;
while (count < num64KPages)
{
// MSDN: "To specify a suggested base address for the view, use the MapViewOfFileEx function. However, this practice is not recommended."
// The OS (ie. another process) may've beaten us to this suggested baseAddr. This is why we retry multiple times.
if (!MapViewOfFileEx(g_hMemImage, FILE_MAP_ALL_ACCESS, 0, 0, _6502_MEM_LEN, baseAddr + count * _6502_MEM_LEN))
break;
count++;
}
res = (count == num64KPages);
if (res)
break;
// Failed this time, so clean-up and retry...
FreeMemImage();
}
while (retry--);
#if 1
if (res) // test
{
baseAddr[0x0000] = 0x11;
baseAddr[0xffff] = 0x22;
USHORT value = *((USHORT*)(baseAddr + 0xffff));
_ASSERT(value == 0x1122);
}
#endif
}
else
{
LogFileOutput("MemInitialize: SYSETEM_INFO.wAllocationGranularity = 0x%08X.\n", info.dwAllocationGranularity);
}
if (!res)
{
LogFileOutput("MemInitialize: Failed to map 2 adjacent virtual 64K pages (reverting to old method).\n");
baseAddr = ALIGNED_ALLOC(_6502_MEM_LEN);
}
return baseAddr;
#else
g_hMemTempFile = tmpfile();
if (g_hMemTempFile)
{
const int fd = fileno(g_hMemTempFile);
if (!ftruncate(fd, _6502_MEM_LEN))
{
LPBYTE baseAddr = static_cast<LPBYTE>(mmap(NULL, num64KPages * _6502_MEM_LEN, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
if (baseAddr)
{
bool ok = true;
for (UINT i = 0; i < num64KPages; i++)
{
void * target = baseAddr + i * _6502_MEM_LEN;
void * addr = mmap(target, _6502_MEM_LEN, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
ok = ok && (target == addr);
}
// we could fclose the file here
// but we keep it as a reminder of how to free the memory later
if (ok)
{
return baseAddr;
}
// revert to ALIGNED_ALLOC
munmap(baseAddr, num64KPages * _6502_MEM_LEN);
}
}
fclose(g_hMemTempFile);
g_hMemTempFile = NULL;
}
LogFileOutput("MemInitialize: Failed to map 2 adjacent virtual 64K pages (reverting to old method).\n");
return ALIGNED_ALLOC(_6502_MEM_LEN);
#endif
}
//===========================================================================
void MemInitialize()
{
// ALLOCATE MEMORY FOR THE APPLE MEMORY IMAGE AND ASSOCIATED DATA STRUCTURES
memaux = ALIGNED_ALLOC(_6502_MEM_LEN); // NB. alloc even if model is Apple II/II+, since it's used by VidHD card
memmain = ALIGNED_ALLOC(_6502_MEM_LEN);
memimage = ALIGNED_ALLOC(_6502_MEM_LEN);
memimage = AllocMemImage();
memdirty = new BYTE[0x100];
memrom = new BYTE[0x3000 * MaxRomPages];
@ -1774,6 +1926,9 @@ void MemInitializeFromSnapshot(void)
// NB. Copied to /mem/ by UpdatePaging(TRUE)
}
GetCardMgr().GetLanguageCardMgr().SetMemModeFromSnapshot();
// Finally setup the paging tables
MemUpdatePaging(TRUE);
//
@ -1946,7 +2101,7 @@ void MemReset()
mem = memimage;
// INITIALIZE PAGING, FILLING IN THE 64K MEMORY IMAGE
ResetPaging(TRUE); // Initialize=1, init memmode
ResetPaging(TRUE); // Initialize=1, init g_memmode
MemAnnunciatorReset();
// INITIALIZE & RESET THE CPU
@ -2010,7 +2165,7 @@ static void DebugFlip(WORD address, ULONG nExecutedCycles)
BYTE __stdcall MemSetPaging(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nExecutedCycles)
{
address &= 0xFF;
DWORD lastmemmode = memmode;
DWORD lastmemmode = g_memmode;
#if defined(_DEBUG) && defined(DEBUG_FLIP_TIMINGS)
DebugFlip(address, nExecutedCycles);
#endif
@ -2020,22 +2175,22 @@ BYTE __stdcall MemSetPaging(WORD programcounter, WORD address, BYTE write, BYTE
{
switch (address)
{
case 0x00: SetMemMode(memmode & ~MF_80STORE); break;
case 0x01: SetMemMode(memmode | MF_80STORE); break;
case 0x02: SetMemMode(memmode & ~MF_AUXREAD); break;
case 0x03: SetMemMode(memmode | MF_AUXREAD); break;
case 0x04: SetMemMode(memmode & ~MF_AUXWRITE); break;
case 0x05: SetMemMode(memmode | MF_AUXWRITE); break;
case 0x06: SetMemMode(memmode & ~MF_INTCXROM); break;
case 0x07: SetMemMode(memmode | MF_INTCXROM); break;
case 0x08: SetMemMode(memmode & ~MF_ALTZP); break;
case 0x09: SetMemMode(memmode | MF_ALTZP); break;
case 0x0A: SetMemMode(memmode & ~MF_SLOTC3ROM); break;
case 0x0B: SetMemMode(memmode | MF_SLOTC3ROM); break;
case 0x54: SetMemMode(memmode & ~MF_PAGE2); break;
case 0x55: SetMemMode(memmode | MF_PAGE2); break;
case 0x56: SetMemMode(memmode & ~MF_HIRES); break;
case 0x57: SetMemMode(memmode | MF_HIRES); break;
case 0x00: SetMemMode(g_memmode & ~MF_80STORE); break;
case 0x01: SetMemMode(g_memmode | MF_80STORE); break;
case 0x02: SetMemMode(g_memmode & ~MF_AUXREAD); break;
case 0x03: SetMemMode(g_memmode | MF_AUXREAD); break;
case 0x04: SetMemMode(g_memmode & ~MF_AUXWRITE); break;
case 0x05: SetMemMode(g_memmode | MF_AUXWRITE); break;
case 0x06: SetMemMode(g_memmode & ~MF_INTCXROM); break;
case 0x07: SetMemMode(g_memmode | MF_INTCXROM); break;
case 0x08: SetMemMode(g_memmode & ~MF_ALTZP); break;
case 0x09: SetMemMode(g_memmode | MF_ALTZP); break;
case 0x0A: SetMemMode(g_memmode & ~MF_SLOTC3ROM); break;
case 0x0B: SetMemMode(g_memmode | MF_SLOTC3ROM); break;
case 0x54: SetMemMode(g_memmode & ~MF_PAGE2); break;
case 0x55: SetMemMode(g_memmode | MF_PAGE2); break;
case 0x56: SetMemMode(g_memmode & ~MF_HIRES); break;
case 0x57: SetMemMode(g_memmode | MF_HIRES); break;
#ifdef RAMWORKS
case 0x71: // extended memory aux page number
case 0x73: // Ramworks III set aux page number
@ -2063,10 +2218,10 @@ BYTE __stdcall MemSetPaging(WORD programcounter, WORD address, BYTE write, BYTE
{
switch (address)
{
case 0x58: SetMemMode(memmode & ~MF_ALTROM0); break;
case 0x59: SetMemMode(memmode | MF_ALTROM0); break;
case 0x5A: SetMemMode(memmode & ~MF_ALTROM1); break;
case 0x5B: SetMemMode(memmode | MF_ALTROM1); break;
case 0x58: SetMemMode(g_memmode & ~MF_ALTROM0); break;
case 0x59: SetMemMode(g_memmode | MF_ALTROM0); break;
case 0x5A: SetMemMode(g_memmode & ~MF_ALTROM1); break;
case 0x5B: SetMemMode(g_memmode | MF_ALTROM1); break;
}
}
@ -2075,10 +2230,10 @@ BYTE __stdcall MemSetPaging(WORD programcounter, WORD address, BYTE write, BYTE
// IF THE MEMORY PAGING MODE HAS CHANGED, UPDATE OUR MEMORY IMAGES AND
// WRITE TABLES.
if ((lastmemmode != memmode) || modechanging)
if ((lastmemmode != g_memmode) || modechanging)
{
// NB. Must check MF_SLOTC3ROM too, as IoHandlerCardsIn() depends on both MF_INTCXROM|MF_SLOTC3ROM
if ((lastmemmode & (MF_INTCXROM|MF_SLOTC3ROM)) != (memmode & (MF_INTCXROM|MF_SLOTC3ROM)))
if ((lastmemmode & (MF_INTCXROM|MF_SLOTC3ROM)) != (g_memmode & (MF_INTCXROM|MF_SLOTC3ROM)))
{
if (!SW_INTCXROM)
{
@ -2120,6 +2275,9 @@ bool MemOptimizeForModeChanging(WORD programcounter, WORD address)
{
if (IsAppleIIeOrAbove(GetApple2Type()))
{
if (programcounter > 0xFFFC) // Prevent out of bounds access!
return false;
// IF THE EMULATED PROGRAM HAS JUST UPDATED THE MEMORY WRITE MODE AND IS
// ABOUT TO UPDATE THE MEMORY READ MODE, HOLD OFF ON ANY PROCESSING UNTIL
// IT DOES SO.
@ -2133,6 +2291,8 @@ bool MemOptimizeForModeChanging(WORD programcounter, WORD address)
return true;
}
// TODO: support Saturn in any slot.
// NB. GH#602 asks for any examples of this happening:
if ((address >= 0x80) && (address <= 0x8F) && (programcounter < 0xC000) && // Now: LC
(((*(LPDWORD)(mem+programcounter) & 0x00FFFEFF) == 0x00C0048D) || // Next: STA $C004(RAMWRTOFF) or STA $C005(RAMWRTON)
((*(LPDWORD)(mem+programcounter) & 0x00FFFEFF) == 0x00C0028D))) // or STA $C002(RAMRDOFF) or STA $C003(RAMRDON)
@ -2162,7 +2322,7 @@ void MemAnnunciatorReset(void)
if (IsCopamBase64A(GetApple2Type()))
{
SetMemMode(memmode & ~(MF_ALTROM0|MF_ALTROM1));
SetMemMode(g_memmode & ~(MF_ALTROM0|MF_ALTROM1));
UpdatePaging(FALSE); // Initialize=FALSE
}
}
@ -2205,6 +2365,8 @@ void MemRemoveNoSlotClock(void)
#define SS_YAML_KEY_EXPANSIONROMTYPE "Expansion ROM Type"
#define SS_YAML_KEY_PERIPHERALROMSLOT "Peripheral ROM Slot"
#define SS_YAML_KEY_ANNUNCIATOR "Annunciator"
#define SS_YAML_KEY_LASTSLOTTOSETMAINMEMLC "Last Slot to Set Main Mem LC"
#define SS_YAML_KEY_MMULCMODE "MMU LC Mode"
//
@ -2269,16 +2431,20 @@ void MemSaveSnapshot(YamlSaveHelper& yamlSaveHelper)
// Scope so that "Memory" & "Main Memory" are at same indent level
{
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", MemGetSnapshotStructName().c_str());
DWORD saveMemMode = memmode;
if (IsApple2PlusOrClone(GetApple2Type()))
saveMemMode &= ~MF_LANGCARD_MASK; // For II,II+: clear LC bits - set later by slot-0 LC or Saturn
yamlSaveHelper.SaveHexUint32(SS_YAML_KEY_MEMORYMODE, saveMemMode);
if (!IsApple2PlusOrClone(GetApple2Type())) // NB. This is set later for II,II+ by slot-0 LC or Saturn
// LC bits
// . For II,II+: set later by slot-0 LC or Saturn
// . For //e,//c: set in SS_YAML_KEY_MMULCMODE
yamlSaveHelper.SaveHexUint32(SS_YAML_KEY_MEMORYMODE, g_memmode & ~MF_LANGCARD_MASK); // Clear LC bits
if (!IsApple2PlusOrClone(GetApple2Type())) // NB. Thesed are set later for II,II+ by slot-0 LC or Saturn
{
yamlSaveHelper.SaveHexUint32(SS_YAML_KEY_MMULCMODE, g_memmode & MF_LANGCARD_MASK);
yamlSaveHelper.SaveUint(SS_YAML_KEY_LASTRAMWRITE, GetLastRamWrite() ? 1 : 0);
}
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_IOSELECT, IO_SELECT);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_IOSELECT_INT, INTC8ROM ? 1 : 0);
yamlSaveHelper.SaveUint(SS_YAML_KEY_EXPANSIONROMTYPE, (UINT) g_eExpansionRomType);
yamlSaveHelper.SaveUint(SS_YAML_KEY_PERIPHERALROMSLOT, g_uPeripheralRomSlot);
yamlSaveHelper.SaveUint(SS_YAML_KEY_LASTSLOTTOSETMAINMEMLC, GetCardMgr().GetLanguageCardMgr().GetLastSlotToSetMainMemLC());
for (UINT i=0; i<kNumAnnunciators; i++)
{
@ -2317,18 +2483,36 @@ bool MemLoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT unitVersion)
if (unitVersion == 1)
{
SetMemMode( yamlLoadHelper.LoadUint(SS_YAML_KEY_MEMORYMODE) ^ MF_INTCXROM ); // Convert from SLOTCXROM to INTCXROM
SetLastRamWrite( yamlLoadHelper.LoadUint(SS_YAML_KEY_LASTRAMWRITE) ? TRUE : FALSE );
UINT uMemMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_MEMORYMODE) ^ MF_INTCXROM; // Convert from SLOTCXROM to INTCXROM
SetMemMode(uMemMode);
if (GetCardMgr().GetLanguageCardMgr().GetLanguageCard())
GetCardMgr().GetLanguageCardMgr().GetLanguageCard()->SetLCMemMode(uMemMode & MF_LANGCARD_MASK);
SetLastRamWrite(yamlLoadHelper.LoadUint(SS_YAML_KEY_LASTRAMWRITE) ? TRUE : FALSE);
}
else
{
UINT uMemMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_MEMORYMODE);
if (IsApple2PlusOrClone(GetApple2Type()))
uMemMode &= ~MF_LANGCARD_MASK; // For II,II+: clear LC bits - set later by slot-0 LC or Saturn
if (IsApple2PlusOrClone(GetApple2Type()) || unitVersion >= 9)
uMemMode &= ~MF_LANGCARD_MASK; // For II,II+: clear LC bits - set later by slot-0 LC or Saturn (or some other slot-n Saturn)
// For //e,//c: (>=v9) clear LC bits - set later after reading all cards and we know which card contributes these bits
// For //e (<9): don't clear, as only old versions only supported the IIe LC from the MMU
SetMemMode(uMemMode);
if (!IsApple2PlusOrClone(GetApple2Type()))
SetLastRamWrite( yamlLoadHelper.LoadUint(SS_YAML_KEY_LASTRAMWRITE) ? TRUE : FALSE ); // NB. This is set later for II,II+ by slot-0 LC or Saturn
if (!IsApple2PlusOrClone(GetApple2Type())) // NB. These are set later for II,II+ by slot-0 LC or Saturn
{
if (unitVersion < 9)
{
GetCardMgr().GetLanguageCardMgr().GetLanguageCard()->SetLCMemMode(uMemMode & MF_LANGCARD_MASK);
}
else
{
UINT LCMemMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_MMULCMODE);
GetCardMgr().GetLanguageCardMgr().GetLanguageCard()->SetLCMemMode(LCMemMode);
}
SetLastRamWrite(yamlLoadHelper.LoadUint(SS_YAML_KEY_LASTRAMWRITE) ? TRUE : FALSE);
}
}
if (unitVersion >= 3)
@ -2340,6 +2524,12 @@ bool MemLoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT unitVersion)
}
}
GetCardMgr().GetLanguageCardMgr().SetLastSlotToSetMainMemLCFromSnapshot(SLOT0);
if (unitVersion >= 9)
{
GetCardMgr().GetLanguageCardMgr().SetLastSlotToSetMainMemLCFromSnapshot(yamlLoadHelper.LoadUint(SS_YAML_KEY_LASTSLOTTOSETMAINMEMLC));
}
yamlLoadHelper.PopMap();
//
@ -2360,11 +2550,8 @@ bool MemLoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT unitVersion)
yamlLoadHelper.PopMap();
//
// NB. MemUpdatePaging(TRUE) called at end of Snapshot_LoadState_v2()
UpdatePaging(1); // Initialize=1 (Still needed, even with call to MemUpdatePaging() - why?)
// TC-TODO: At this point, the cards haven't been loaded, so the card's expansion ROM is unknown - so pointless(?) calling this now
// NB. MemInitializeFromSnapshot()->MemUpdatePaging() called at end of Snapshot_LoadState_v2()
// . At this point, the cards haven't been loaded (no aux mem & any card's expansion ROM is unknown) - so pointless calling MemUpdatePaging() at this stage (GH#1267)
return true;
}

View File

@ -115,6 +115,7 @@ SS_CARDTYPE GetCurrentExpansionMemType(void);
void SetRamWorksMemorySize(UINT pages);
UINT GetRamWorksActiveBank(void);
void SetMemMainLanguageCard(LPBYTE ptr, bool bMemMain=false);
void SetMemMainLanguageCard(LPBYTE ptr, UINT slot, bool bMemMain=false);
LPBYTE GetCxRomPeripheral(void);
UINT GetLastSlotToSetMainMemLC(void);

View File

@ -226,12 +226,9 @@ void MockingboardCard::WriteToORB(BYTE subunit, BYTE subunitForAY/*=0*/)
#else
if (m_phasorEnable)
{
const int kAY1 = 2; // Phasor mode: bit4=0 (active low) selects the 1st AY8913, ie. the only AY8913 in Mockingboard mode (confirmed on real Phasor h/w)
// Echo+ mode: bit3=1 (active high) selects the 1st AY8913
const int kAY2 = 1; // Phasor mode: bit3=0 (active low) selects the 2nd AY8913 attached to this 6522 (unavailable in Mockingboard mode)
// Echo+ mode: bit4=1 (active high) selects the 2nd AY8913
const int nAY_CS = (m_phasorMode == PH_EchoPlus) ? ((value >> 4) & 1) | ((value >> 2) & 2) // swap bits 4 & 3
: (m_phasorMode == PH_Phasor) ? (~(value >> 3) & 3)
const int kAY1 = 2; // Phasor/Echo+ mode: bit4=0 (active low) selects the 1st AY8913, ie. the only AY8913 in Mockingboard mode (confirmed on real Phasor h/w)
const int kAY2 = 1; // Phasor/Echo+ mode: bit3=0 (active low) selects the 2nd AY8913 attached to this 6522 (unavailable in Mockingboard mode)
const int nAY_CS = (m_phasorMode == PH_Phasor || m_phasorMode == PH_EchoPlus) ? (~(value >> 3) & 3)
: kAY1; // Anything else is Mockingboard
if (m_phasorMode == PH_EchoPlus)
@ -252,13 +249,16 @@ void MockingboardCard::WriteToORB(BYTE subunit, BYTE subunitForAY/*=0*/)
AY8913_Write(subunit, AY8913_DEVICE_B, value);
if (nAY_CS == 0)
m_MBSubUnit[subunit].sy6522.UpdatePortAForHiZ();
m_MBSubUnit[subunit].SetBusState(false);
}
else
{
if ((value & 4) == 0)
{
AY8913_Reset(subunit);
if (QueryType() == CT_SDMusic)
AY8913_Reset(subunitForAY); // to do: check that AYs can be independently reset
else
AY8913_Reset(subunit);
return;
}
@ -279,6 +279,9 @@ void MockingboardCard::AY8913_Reset(BYTE subunit)
AY8910_reset(subunit, AY8913_DEVICE_B); // GH#1197: Reset both AYs regardless of Phasor mode & chip-select bits
m_MBSubUnit[subunit].Reset(QueryType());
if (QueryType() == CT_SDMusic)
m_MBSubUnit[0].SetBusState(false);
}
void MockingboardCard::AY8913_Write(BYTE subunit, BYTE ay, BYTE value)
@ -286,6 +289,7 @@ void MockingboardCard::AY8913_Write(BYTE subunit, BYTE ay, BYTE value)
m_regAccessedFlag = true;
MB_SUBUNIT* pMB = &m_MBSubUnit[subunit];
SY6522& r6522 = (QueryType() != CT_SDMusic) ? pMB->sy6522 : m_MBSubUnit[0].sy6522;
bool busState = false; // Default: Mockingboard or Phasor(any mode) will read PortA inputs as high.
// Determine the AY8913 inputs
int nBDIR = (value & 2) ? 1 : 0;
@ -299,7 +303,8 @@ void MockingboardCard::AY8913_Write(BYTE subunit, BYTE ay, BYTE value)
if (!m_phasorEnable || m_phasorMode == PH_Mockingboard)
_ASSERT(ay == AY8913_DEVICE_A);
if (nAYFunc == AY_READ || nAYFunc == AY_WRITE || nAYFunc == AY_LATCH)
_ASSERT(state == AY_INACTIVE);
if ((nAYFunc != state) || (m_phasorEnable && m_phasorMode != PH_EchoPlus)) // Deater's Xmas2023 demo interleaves writes to both AY's (need this line to avoid ASSERT for Echo+)
_ASSERT(state == AY_INACTIVE);
#endif
if (state == AY_INACTIVE) // GH#320: functions only work from inactive state
@ -310,24 +315,20 @@ void MockingboardCard::AY8913_Write(BYTE subunit, BYTE ay, BYTE value)
break;
case AY_READ: // 5: READ FROM PSG (need to set DDRA to input)
if (QueryType() != CT_MegaAudio)
{
if (pMB->isChipSelected[ay] && pMB->isAYLatchedAddressValid[ay])
r6522.SetRegORA(AYReadReg(subunit, ay, pMB->nAYCurrentRegister[ay]) & (r6522.GetReg(SY6522::rDDRA) ^ 0xff));
else
r6522.UpdatePortAForHiZ();
}
else
{
r6522.SetRegORA(0x00); // Reads not supported.
}
if (m_phasorEnable && m_phasorMode == PH_Phasor) // GH#1192
{
if (ay == AY8913_DEVICE_A)
{
if (pMB->isChipSelected[AY8913_DEVICE_B] && pMB->isAYLatchedAddressValid[AY8913_DEVICE_B])
r6522.SetRegORA(r6522.GetReg(SY6522::rORA) | (AYReadReg(subunit, AY8913_DEVICE_B, pMB->nAYCurrentRegister[AY8913_DEVICE_B]) & (r6522.GetReg(SY6522::rDDRA) ^ 0xff)));
r6522.SetRegIRA(AYReadReg(subunit, ay, pMB->nAYCurrentRegister[ay]) & (r6522.GetReg(SY6522::rDDRA) ^ 0xff));
busState = true;
}
if (m_phasorEnable && m_phasorMode == PH_Phasor) // GH#1192
{
if (ay == AY8913_DEVICE_A)
{
if (pMB->isChipSelected[AY8913_DEVICE_B] && pMB->isAYLatchedAddressValid[AY8913_DEVICE_B])
r6522.SetRegIRA(r6522.GetReg(SY6522::rORA) | (AYReadReg(subunit, AY8913_DEVICE_B, pMB->nAYCurrentRegister[AY8913_DEVICE_B]) & (r6522.GetReg(SY6522::rDDRA) ^ 0xff)));
}
}
}
break;
@ -379,8 +380,10 @@ void MockingboardCard::AY8913_Write(BYTE subunit, BYTE ay, BYTE value)
state = nAYFunc;
if (state == AY_INACTIVE && m_phasorEnable) // Phasor(even in MB mode) will read PortA inputs as high.
r6522.UpdatePortAForHiZ(); // Float high any PortA input bits (GH#1193)
if (QueryType() == CT_SDMusic)
m_MBSubUnit[0].SetBusState(busState);
else
pMB->SetBusState(busState);
}
//-----------------------------------------------------------------------------
@ -1151,7 +1154,8 @@ UINT MockingboardCard::AY8910_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, BYTE
// "Chip Select A" + "Chip Select B"
// "Reg Address Latch Valid A" + "Reg Address Latch Valid B"
// Changed at AppleWin 1.30.14
const UINT kUNIT_VERSION = 10;
//11: Added: "Bus Driven by AY"
const UINT kUNIT_VERSION = 11;
#define SS_YAML_KEY_MB_UNIT "Unit"
#define SS_YAML_KEY_AY_CURR_REG "AY Current Register"
@ -1167,6 +1171,7 @@ const UINT kUNIT_VERSION = 10;
#define SS_YAML_KEY_SPEECH_IRQ "Speech IRQ Pending" // v8: deprecated
#define SS_YAML_KEY_TIMER1_ACTIVE "Timer1 Active" // v8: move to 6522 sub-unit
#define SS_YAML_KEY_TIMER2_ACTIVE "Timer2 Active" // v8: move to 6522 sub-unit
#define SS_YAML_KEY_BUS_DRIVEN "Bus Driven by AY"
#define SS_YAML_KEY_PHASOR_UNIT "Unit"
#define SS_YAML_KEY_PHASOR_CLOCK_SCALE_FACTOR "Clock Scale Factor" // v6: deprecated
@ -1231,6 +1236,7 @@ void MockingboardCard::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_AY_CURR_REG, pMB->nAYCurrentRegister[0]); // save all 8 bits (even though top 4 bits should be 0)
yamlSaveHelper.SaveBool(SS_YAML_KEY_CS_A, pMB->isChipSelected[0]);
yamlSaveHelper.SaveBool(SS_YAML_KEY_LATCH_ADDR_VALID_A, pMB->isAYLatchedAddressValid[0]);
yamlSaveHelper.SaveBool(SS_YAML_KEY_BUS_DRIVEN, pMB->isBusDriven);
}
}
@ -1297,6 +1303,11 @@ bool MockingboardCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version
pMB->isAYLatchedAddressValid[0] = yamlLoadHelper.LoadBool(SS_YAML_KEY_LATCH_ADDR_VALID_A);
}
bool busState = (pMB->state[0] == AY_READ);
if (version >= 11)
busState = yamlLoadHelper.LoadBool(SS_YAML_KEY_BUS_DRIVEN);
pMB->SetBusState(busState);
yamlLoadHelper.PopMap();
}
@ -1336,6 +1347,8 @@ void MockingboardCard::Phasor_SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
yamlSaveHelper.SaveBool(SS_YAML_KEY_CS_B, pMB->isChipSelected[1]);
yamlSaveHelper.SaveBool(SS_YAML_KEY_LATCH_ADDR_VALID_A, pMB->isAYLatchedAddressValid[0]);
yamlSaveHelper.SaveBool(SS_YAML_KEY_LATCH_ADDR_VALID_B, pMB->isAYLatchedAddressValid[1]);
yamlSaveHelper.SaveBool(SS_YAML_KEY_BUS_DRIVEN, pMB->isBusDriven);
}
}
@ -1430,6 +1443,11 @@ bool MockingboardCard::Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT
pMB->isAYLatchedAddressValid[1] = yamlLoadHelper.LoadBool(SS_YAML_KEY_LATCH_ADDR_VALID_B);
}
bool busState = (pMB->state[0] == AY_READ || pMB->state[1] == AY_READ);
if (version >= 11)
busState = yamlLoadHelper.LoadBool(SS_YAML_KEY_BUS_DRIVEN);
pMB->SetBusState(busState);
yamlLoadHelper.PopMap();
}

View File

@ -87,6 +87,7 @@ private:
MockingboardUnitState_e state[NUM_AY8913_PER_SUBUNIT]; // AY's PSG function
bool isAYLatchedAddressValid[NUM_AY8913_PER_SUBUNIT];
bool isChipSelected[NUM_AY8913_PER_SUBUNIT];
bool isBusDriven;
MB_SUBUNIT(UINT slot, SS_CARDTYPE type) : sy6522(slot, type == CT_MegaAudio), ssi263(slot)
{
@ -102,6 +103,13 @@ private:
isAYLatchedAddressValid[0] = isAYLatchedAddressValid[1] = false; // after AY reset
isChipSelected[0] = type == CT_Phasor ? false : true; // Only Phasor is false, all other MB variants are true
isChipSelected[1] = false;
SetBusState(false);
}
void SetBusState(bool state)
{
isBusDriven = state;
sy6522.SetBusBeingDriven(state);
}
};

View File

@ -127,6 +127,7 @@ BYTE SSI263::Read(ULONG nExecutedCycles)
{
// Regardless of register, just return inverted A/!R in bit7
// . inverted "A/!R" is high for REQ (ie. Request, as phoneme nearly complete)
// NB. this doesn't clear the IRQ
return MemReadFloatingBus(m_currentMode & 1, nExecutedCycles);
}
@ -140,6 +141,25 @@ void SSI263::Write(BYTE nReg, BYTE nValue)
ssiRegs[nReg] = nValue;
#endif
// Notes:
// . Phasor's text-to-speech playback has no CTL H->L
// - ISR just writes CTL=0 (and new ART+AMP values), and writes DUR=x (and new PHON)
// - since no CTL H->L, then DUR value doesn't take affect (so continue using previous)
// - so the write to DURPHON must clear the IRQ
// . Does a write of CTL=0 clear IRQ? (ie. CTL 0->0)
// . Does a write of CTL=1 clear IRQ? (ie. CTL 0->1)
// - SSI263 datasheet says: "Setting the Control bit (CTL) to a logic one puts the device into Power Down mode..."
// . Does phoneme output only happen when CTL=0? (Otherwise device is in PD mode)
// SSI263 datasheet is not clear, but a write to DURPHON must clear the IRQ.
// . Empirically writes to regs 0,1 & 2 all clear the IRQ (and writes to 3,4..7 don't) (GH#1197)
// NB. For Mockingboard, A/!R is ack'ed by 6522's PCR handshake and D7 is cleared.
if (m_cardMode == PH_Phasor && nReg <= SSI_RATEINF)
{
CpuIrqDeassert(IS_SPEECH);
m_currentMode &= ~1; // Clear SSI263's D7 pin
}
switch(nReg)
{
case SSI_DURPHON:
@ -151,25 +171,6 @@ void SSI263::Write(BYTE nReg, BYTE nValue)
SSI_Output();
#endif
// Notes:
// . Phasor's text-to-speech playback has no CTL H->L
// - ISR just writes CTL=0 (and new ART+AMP values), and writes DUR=x (and new PHON)
// - since no CTL H->L, then DUR value doesn't take affect (so continue using previous)
// - so the write to DURPHON must clear the IRQ
// . Does a write of CTL=0 clear IRQ? (ie. CTL 0->0)
// . Does a write of CTL=1 clear IRQ? (ie. CTL 0->1)
// - SSI263 datasheet says: "Setting the Control bit (CTL) to a logic one puts the device into Power Down mode..."
// . Does phoneme output only happen when CTL=0? (Otherwise device is in PD mode)
// SSI263 datasheet is not clear, but a write to DURPHON must clear the IRQ.
// NB. For Mockingboard, A/!R is ack'ed by 6522's PCR handshake.
if (m_cardMode == PH_Phasor)
{
CpuIrqDeassert(IS_SPEECH);
}
m_currentMode &= ~1; // Clear SSI263's D7 pin
m_durationPhoneme = nValue;
Play(nValue & PHONEME_MASK);
@ -323,6 +324,11 @@ void SSI263::Votrax_Write(BYTE value)
void SSI263::Play(unsigned int nPhoneme)
{
if (!SSI263SingleVoice.lpDSBvoice)
{
return;
}
if (!SSI263SingleVoice.bActive)
{
bool bRes = DSZeroVoiceBuffer(&SSI263SingleVoice, m_kDSBufferByteSize);
@ -900,7 +906,7 @@ void SSI263::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, PHASOR_MODE mode, UINT
//
_ASSERT(m_device != -1);
_ASSERT(m_device != BYTE(-1));
SetCardMode(mode);
// Only need to directly assert IRQ for Phasor mode (for Mockingboard mode it's done via UpdateIFR() in parent)

View File

@ -67,7 +67,8 @@ static YamlHelper yamlHelper;
// v6: Added 'Unit Miscellaneous' for NoSlotClock(NSC)
// v7: Extended: joystick (added 'Paddle Inactive Cycle')
// v8: Added 'Unit Game I/O Connector' for Game I/O Connector
#define UNIT_APPLE2_VER 8
// v9: Extended: memory (added 'Last Slot to Set Main Mem LC', 'MMU LC Mode')
#define UNIT_APPLE2_VER 9
#define UNIT_SLOTS_VER 1
@ -292,7 +293,7 @@ static void ParseSlots(YamlLoadHelper& yamlLoadHelper, UINT unitVersion)
SS_CARDTYPE type = Card::GetCardType(card);
bool bRes = false;
if (slot == 0)
if (slot == SLOT0)
{
SetExpansionMemType(type); // calls GetCardMgr().Insert() & InsertAux()
}

View File

@ -47,6 +47,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#define TCP_SERIAL_PORT 1977
const UINT CSuperSerialCard::SERIALPORTITEM_INVALID_COM_PORT = 0;
// Default: 9600-8-N-1
SSC_DIPSW CSuperSerialCard::m_DIPSWDefault =
{
@ -69,7 +71,8 @@ CSuperSerialCard::CSuperSerialCard(UINT slot) :
m_strSerialPortChoices(1, '\0'), // Combo box friendly, just in case.
m_uTCPChoiceItemIdx(0),
m_bCfgSupportDCD(false),
m_pExpansionRom(NULL)
m_pExpansionRom(NULL),
m_hFrameWindow(NULL)
{
if (m_slot != 2) // fixme
ThrowErrorInvalidSlot();
@ -241,6 +244,7 @@ bool CSuperSerialCard::CheckComm()
saAddress.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(m_hCommListenSocket, (LPSOCKADDR)&saAddress, sizeof(saAddress)) == SOCKET_ERROR)
{
closesocket(m_hCommListenSocket);
m_hCommListenSocket = INVALID_SOCKET;
WSACleanup();
return false;
@ -249,18 +253,21 @@ bool CSuperSerialCard::CheckComm()
// bound, so listen
if (listen(m_hCommListenSocket, 1) == SOCKET_ERROR)
{
closesocket(m_hCommListenSocket);
m_hCommListenSocket = INVALID_SOCKET;
WSACleanup();
return false;
}
// now send async events to our app's message handler
m_hFrameWindow = GetFrame().g_hFrameWindow;
if (WSAAsyncSelect(
/* SOCKET s */ m_hCommListenSocket,
/* HWND hWnd */ GetFrame().g_hFrameWindow,
/* HWND hWnd */ m_hFrameWindow,
/* unsigned int wMsg */ WM_USER_TCP_SERIAL,
/* long lEvent */ (FD_ACCEPT | FD_CONNECT | FD_READ | FD_CLOSE)) != 0)
{
closesocket(m_hCommListenSocket);
m_hCommListenSocket = INVALID_SOCKET;
WSACleanup();
return false;
@ -328,7 +335,7 @@ void CSuperSerialCard::CommTcpSerialCleanup()
{
if (m_hCommListenSocket != INVALID_SOCKET)
{
WSAAsyncSelect(m_hCommListenSocket, GetFrame().g_hFrameWindow, 0, 0); // Stop event messages
WSAAsyncSelect(m_hCommListenSocket, m_hFrameWindow, 0, 0); // Stop event messages
closesocket(m_hCommListenSocket);
m_hCommListenSocket = INVALID_SOCKET;

View File

@ -88,7 +88,7 @@ private:
std::string m_currentSerialPortName;
DWORD m_dwSerialPortItem;
static const UINT SERIALPORTITEM_INVALID_COM_PORT = 0;
static const UINT SERIALPORTITEM_INVALID_COM_PORT;
std::vector<UINT> m_vecSerialPortsItems; // Includes "None" & "TCP" items
std::string m_strSerialPortChoices;
UINT m_uTCPChoiceItemIdx;
@ -111,6 +111,7 @@ private:
HANDLE m_hCommHandle;
SOCKET m_hCommListenSocket;
SOCKET m_hCommAcceptSocket;
HWND m_hFrameWindow; // to avoid variable (de)-initialisation order issues
//

View File

@ -53,7 +53,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// their buffers are running low.
//
static const unsigned short g_nSPKR_NumChannels = 1;
// NB. Setting g_nSPKR_NumChannels=1 still works (ie. mono).
// . Retain it for a while in case there are regressions with the new 2-channel code, then remove it.
static const unsigned short g_nSPKR_NumChannels = 2;
static const DWORD g_dwDSSpkrBufferSize = MAX_SAMPLES * sizeof(short) * g_nSPKR_NumChannels;
//-------------------------------------
@ -64,7 +66,7 @@ static short* g_pSpeakerBuffer = NULL;
const short SPKR_DATA_INIT = (short)0x8000;
short g_nSpeakerData = SPKR_DATA_INIT;
static UINT g_nBufferIdx = 0;
static UINT g_nBufferIdx = 0; // Sample index
static short* g_pRemainderBuffer = NULL;
static UINT g_nRemainderBufferSize; // Setup in SpkrInitialize()
@ -101,6 +103,11 @@ void Spkr_OutputToRiff(void)
g_bSpkrOutputToRiff = true;
}
UINT Spkr_GetNumChannels(void)
{
return g_nSPKR_NumChannels;
}
//=============================================================================
static void DisplayBenchmarkResults ()
@ -269,7 +276,7 @@ void SpkrInitialize ()
{
InitRemainderBuffer();
g_pSpeakerBuffer = new short [SPKR_SAMPLE_RATE]; // Buffer can hold a max of 1 seconds worth of samples
g_pSpeakerBuffer = new short [SPKR_SAMPLE_RATE * g_nSPKR_NumChannels]; // Buffer can hold a max of 1 seconds worth of samples
}
}
@ -341,8 +348,20 @@ static void UpdateRemainderBuffer(ULONG* pnCycleDiff)
nSampleMean += (signed long) g_pRemainderBuffer[i];
nSampleMean /= (signed long) g_nRemainderBufferSize;
if(g_nBufferIdx < SPKR_SAMPLE_RATE-1)
g_pSpeakerBuffer[g_nBufferIdx++] = DCFilter( (short)nSampleMean );
if (g_nBufferIdx < SPKR_SAMPLE_RATE - 1)
{
if (g_nSPKR_NumChannels == 1)
{
g_pSpeakerBuffer[g_nBufferIdx] = DCFilter((short)nSampleMean);
}
else
{
const short sample = DCFilter((short)nSampleMean);
g_pSpeakerBuffer[g_nBufferIdx * 2 + 0] = sample;
g_pSpeakerBuffer[g_nBufferIdx * 2 + 1] = sample;
}
g_nBufferIdx++;
}
}
}
}
@ -359,8 +378,20 @@ static void UpdateSpkr()
ULONG nCyclesRemaining = (ULONG) ((double)nCycleDiff - (double)nNumSamples * g_fClksPerSpkrSample);
while((nNumSamples--) && (g_nBufferIdx < SPKR_SAMPLE_RATE-1))
g_pSpeakerBuffer[g_nBufferIdx++] = DCFilter(g_nSpeakerData);
while ((nNumSamples--) && (g_nBufferIdx < SPKR_SAMPLE_RATE - 1))
{
if (g_nSPKR_NumChannels == 1)
{
g_pSpeakerBuffer[g_nBufferIdx] = DCFilter(g_nSpeakerData);
}
else
{
const short sample = DCFilter(g_nSpeakerData);
g_pSpeakerBuffer[g_nBufferIdx * 2 + 0] = sample;
g_pSpeakerBuffer[g_nBufferIdx * 2 + 1] = sample;
}
g_nBufferIdx++;
}
ReinitRemainderBuffer(nCyclesRemaining); // Partially fill 1Mhz sample buffer
}
@ -454,7 +485,7 @@ void SpkrUpdate (DWORD totalcycles)
nSamplesUsed = Spkr_SubmitWaveBuffer(g_pSpeakerBuffer, g_nBufferIdx);
_ASSERT(nSamplesUsed <= g_nBufferIdx);
memmove(g_pSpeakerBuffer, &g_pSpeakerBuffer[nSamplesUsed], g_nBufferIdx-nSamplesUsed); // FIXME-TC: _Size * 2
memmove(g_pSpeakerBuffer, &g_pSpeakerBuffer[nSamplesUsed], (g_nBufferIdx - nSamplesUsed) * sizeof(short) * g_nSPKR_NumChannels);
g_nBufferIdx -= nSamplesUsed;
}
}
@ -470,7 +501,7 @@ void SpkrUpdate_Timer()
nSamplesUsed = Spkr_SubmitWaveBuffer_FullSpeed(g_pSpeakerBuffer, g_nBufferIdx);
_ASSERT(nSamplesUsed <= g_nBufferIdx);
memmove(g_pSpeakerBuffer, &g_pSpeakerBuffer[nSamplesUsed], g_nBufferIdx-nSamplesUsed); // FIXME-TC: _Size * 2 (GH#213?)
memmove(g_pSpeakerBuffer, &g_pSpeakerBuffer[nSamplesUsed], (g_nBufferIdx - nSamplesUsed) * sizeof(short) * g_nSPKR_NumChannels);
g_nBufferIdx -= nSamplesUsed;
}
}
@ -559,7 +590,7 @@ static ULONG Spkr_SubmitWaveBuffer_FullSpeed(short* pSpeakerBuffer, ULONG nNumSa
if(nBytesRemaining < g_dwDSSpkrBufferSize / 4)
{
// < 1/4 of play-buffer remaining (need *more* data)
nNumPadSamples = ((g_dwDSSpkrBufferSize / 4) - nBytesRemaining) / sizeof(short);
nNumPadSamples = ((g_dwDSSpkrBufferSize / 4) - nBytesRemaining) / (sizeof(short) * g_nSPKR_NumChannels);
if(nNumPadSamples > nNumSamples)
nNumPadSamples -= nNumSamples;
@ -574,15 +605,15 @@ static ULONG Spkr_SubmitWaveBuffer_FullSpeed(short* pSpeakerBuffer, ULONG nNumSa
UINT nBytesFree = g_dwDSSpkrBufferSize - nBytesRemaining; // Calc free buffer space
ULONG nNumSamplesToUse = nNumSamples + nNumPadSamples;
if(nNumSamplesToUse * sizeof(short) > nBytesFree)
nNumSamplesToUse = nBytesFree / sizeof(short);
if (nNumSamplesToUse * sizeof(short) * g_nSPKR_NumChannels > nBytesFree)
nNumSamplesToUse = nBytesFree / (sizeof(short) * g_nSPKR_NumChannels);
//
if(nNumSamplesToUse >= 128) // Limit the buffer unlock/locking to a minimum
{
hr = DSGetLock(SpeakerVoice.lpDSBvoice,
dwByteOffset, (DWORD)nNumSamplesToUse * sizeof(short),
dwByteOffset, (DWORD)nNumSamplesToUse * sizeof(short) * g_nSPKR_NumChannels,
&pDSLockedBuffer0, &dwDSLockedBufferSize0,
&pDSLockedBuffer1, &dwDSLockedBufferSize1);
if (FAILED(hr))
@ -597,15 +628,15 @@ static ULONG Spkr_SubmitWaveBuffer_FullSpeed(short* pSpeakerBuffer, ULONG nNumSa
{
//LogOutput("[Submit_FS] C=%08X, PC=%08X, WC=%08X, Diff=%08X, Off=%08X, NS=%08X ***\n", nDbgSpkrCnt, dwCurrentPlayCursor, dwCurrentWriteCursor, dwCurrentWriteCursor-dwCurrentPlayCursor, dwByteOffset, nNumSamples);
if(nNumSamples*sizeof(short) <= dwDSLockedBufferSize0)
if (nNumSamples * sizeof(short) * g_nSPKR_NumChannels <= dwDSLockedBufferSize0)
{
dwBufferSize0 = nNumSamples*sizeof(short);
dwBufferSize0 = nNumSamples * sizeof(short) * g_nSPKR_NumChannels;
dwBufferSize1 = 0;
}
else
{
dwBufferSize0 = dwDSLockedBufferSize0;
dwBufferSize1 = nNumSamples*sizeof(short) - dwDSLockedBufferSize0;
dwBufferSize1 = nNumSamples * sizeof(short) * g_nSPKR_NumChannels - dwDSLockedBufferSize0;
if(dwBufferSize1 > dwDSLockedBufferSize1)
dwBufferSize1 = dwDSLockedBufferSize1;
@ -613,15 +644,15 @@ static ULONG Spkr_SubmitWaveBuffer_FullSpeed(short* pSpeakerBuffer, ULONG nNumSa
memcpy(pDSLockedBuffer0, &pSpeakerBuffer[0], dwBufferSize0);
if (g_bSpkrOutputToRiff)
RiffPutSamples(pDSLockedBuffer0, dwBufferSize0/sizeof(short));
nNumSamples = dwBufferSize0/sizeof(short);
RiffPutSamples(pDSLockedBuffer0, dwBufferSize0 / (sizeof(short) * g_nSPKR_NumChannels));
nNumSamples = dwBufferSize0 / (sizeof(short) * g_nSPKR_NumChannels);
if(pDSLockedBuffer1 && dwBufferSize1)
{
memcpy(pDSLockedBuffer1, &pSpeakerBuffer[dwDSLockedBufferSize0/sizeof(short)], dwBufferSize1);
if (g_bSpkrOutputToRiff)
RiffPutSamples(pDSLockedBuffer1, dwBufferSize1/sizeof(short));
nNumSamples += dwBufferSize1/sizeof(short);
RiffPutSamples(pDSLockedBuffer1, dwBufferSize1 / (sizeof(short) * g_nSPKR_NumChannels));
nNumSamples += dwBufferSize1 / (sizeof(short) * g_nSPKR_NumChannels);
}
}
@ -634,16 +665,44 @@ static ULONG Spkr_SubmitWaveBuffer_FullSpeed(short* pSpeakerBuffer, ULONG nNumSa
if(dwBufferSize0)
{
std::fill_n(pDSLockedBuffer0, dwBufferSize0/sizeof(short), DCFilter(g_nSpeakerData));
const UINT numSamples = dwBufferSize0 / (sizeof(short) * g_nSPKR_NumChannels);
if (g_nSPKR_NumChannels == 1)
{
std::fill_n(pDSLockedBuffer0, numSamples, DCFilter(g_nSpeakerData));
}
else
{
for (UINT i = 0; i < numSamples; i++)
{
const short sample = DCFilter(g_nSpeakerData);
pDSLockedBuffer0[i * 2 + 0] = sample;
pDSLockedBuffer0[i * 2 + 1] = sample;
}
}
if (g_bSpkrOutputToRiff)
RiffPutSamples(pDSLockedBuffer0, dwBufferSize0/sizeof(short));
RiffPutSamples(pDSLockedBuffer0, numSamples);
}
if(pDSLockedBuffer1)
{
std::fill_n(pDSLockedBuffer1, dwBufferSize1/sizeof(short), DCFilter(g_nSpeakerData));
const UINT numSamples = dwBufferSize0 / (sizeof(short) * g_nSPKR_NumChannels);
if (g_nSPKR_NumChannels == 1)
{
std::fill_n(pDSLockedBuffer1, numSamples, DCFilter(g_nSpeakerData));
}
else
{
for (UINT i = 0; i < numSamples; i++)
{
const short sample = DCFilter(g_nSpeakerData);
pDSLockedBuffer1[i * 2 + 0] = sample;
pDSLockedBuffer1[i * 2 + 1] = sample;
}
}
if (g_bSpkrOutputToRiff)
RiffPutSamples(pDSLockedBuffer1, dwBufferSize1/sizeof(short));
RiffPutSamples(pDSLockedBuffer1, numSamples);
}
}
@ -777,7 +836,7 @@ static ULONG Spkr_SubmitWaveBuffer(short* pSpeakerBuffer, ULONG nNumSamples)
//LogOutput("[Submit] C=%08X, PC=%08X, WC=%08X, Diff=%08X, Off=%08X, NS=%08X +++\n", nDbgSpkrCnt, dwCurrentPlayCursor, dwCurrentWriteCursor, dwCurrentWriteCursor-dwCurrentPlayCursor, dwByteOffset, nNumSamplesToUse);
hr = DSGetLock(SpeakerVoice.lpDSBvoice,
dwByteOffset, (DWORD)nNumSamplesToUse * sizeof(short),
dwByteOffset, (DWORD)nNumSamplesToUse * sizeof(short) * g_nSPKR_NumChannels,
&pDSLockedBuffer0, &dwDSLockedBufferSize0,
&pDSLockedBuffer1, &dwDSLockedBufferSize1);
if (FAILED(hr))
@ -788,13 +847,13 @@ static ULONG Spkr_SubmitWaveBuffer(short* pSpeakerBuffer, ULONG nNumSamples)
memcpy(pDSLockedBuffer0, &pSpeakerBuffer[0], dwDSLockedBufferSize0);
if (g_bSpkrOutputToRiff)
RiffPutSamples(pDSLockedBuffer0, dwDSLockedBufferSize0/sizeof(short));
RiffPutSamples(pDSLockedBuffer0, dwDSLockedBufferSize0 / (sizeof(short) * g_nSPKR_NumChannels));
if(pDSLockedBuffer1)
{
memcpy(pDSLockedBuffer1, &pSpeakerBuffer[dwDSLockedBufferSize0/sizeof(short)], dwDSLockedBufferSize1);
if (g_bSpkrOutputToRiff)
RiffPutSamples(pDSLockedBuffer1, dwDSLockedBufferSize1/sizeof(short));
RiffPutSamples(pDSLockedBuffer1, dwDSLockedBufferSize1 / (sizeof(short) * g_nSPKR_NumChannels));
}
// Commit sound buffer
@ -901,7 +960,7 @@ bool Spkr_DSInit()
SpeakerVoice.bIsSpeaker = true;
HRESULT hr = DSGetSoundBuffer(&SpeakerVoice, DSBCAPS_CTRLVOLUME, g_dwDSSpkrBufferSize, SPKR_SAMPLE_RATE, 1, "Spkr");
HRESULT hr = DSGetSoundBuffer(&SpeakerVoice, DSBCAPS_CTRLVOLUME, g_dwDSSpkrBufferSize, SPKR_SAMPLE_RATE, g_nSPKR_NumChannels, "Spkr");
if (FAILED(hr))
{
LogFileOutput("Spkr_DSInit: DSGetSoundBuffer failed (%08X)\n", hr);

View File

@ -31,6 +31,7 @@ void Spkr_Unmute();
bool Spkr_IsActive();
bool Spkr_DSInit();
void Spkr_OutputToRiff(void);
UINT Spkr_GetNumChannels(void);
void SpkrSaveSnapshot(class YamlSaveHelper& yamlSaveHelper);
void SpkrLoadSnapshot(class YamlLoadHelper& yamlLoadHelper);

View File

@ -72,19 +72,12 @@ typedef int socklen_t;
#include <arpa/inet.h>
#include <poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <errno.h>
#endif
// fix SOCK_NONBLOCK for e.g. macOS
#ifndef SOCK_NONBLOCK
// DISCALIMER
// totally untested, use at your own risk
#include <fcntl.h>
#define SOCK_NONBLOCK O_NONBLOCK
#endif
// Dest MAC + Source MAC + Ether Type
#define ETH_MINIMUM_SIZE (6 + 6 + 2)
@ -912,11 +905,8 @@ void Uthernet2::resetRXTXBuffers(const size_t i)
void Uthernet2::openSystemSocket(const size_t i, const int type, const int protocol, const int status)
{
Socket &s = mySockets[i];
#ifdef _MSC_VER
const Socket::socket_t fd = socket(AF_INET, type, protocol);
#else
const Socket::socket_t fd = socket(AF_INET, type | SOCK_NONBLOCK, protocol);
#endif
if (fd == INVALID_SOCKET)
{
#ifdef U2_LOG_STATE
@ -929,9 +919,23 @@ void Uthernet2::openSystemSocket(const size_t i, const int type, const int proto
{
#ifdef _MSC_VER
u_long on = 1;
ioctlsocket(fd, FIONBIO, &on);
const int res = ioctlsocket(fd, FIONBIO, &on);
#else
const int current = fcntl(fd, F_GETFL);
const int res = fcntl(fd, F_SETFL, current | O_NONBLOCK);
#endif
s.setFD(fd, status);
if (res)
{
#ifdef U2_LOG_STATE
const char *proto = (status == W5100_SN_SR_SOCK_UDP) ? "UDP" : "TCP";
LogFileOutput("U2: %s[%" SIZE_T_FMT "]: socket error: %" ERROR_FMT "\n", proto, i, STRERROR(sock_error()));
#endif
s.clearFD();
}
else
{
s.setFD(fd, status);
}
}
}

View File

@ -491,7 +491,7 @@ void GetAppleWindowTitle()
if (g_hCustomRomF8 != INVALID_HANDLE_VALUE)
g_pAppTitle += TEXT(" (custom rom)");
else if (GetPropertySheet().GetTheFreezesF8Rom() && IS_APPLE2)
else if (GetPropertySheet().GetTheFreezesF8Rom() && IsApple2PlusOrClone(GetApple2Type()))
g_pAppTitle += TEXT(" (The Freeze's non-autostart F8 rom)");
switch (g_nAppMode)
@ -546,9 +546,10 @@ void ResetMachineState()
// todo: consolidate CtrlReset() and ResetMachineState()
void CtrlReset()
{
if (!IS_APPLE2)
if (IsAppleIIeOrAbove(GetApple2Type()))
{
// For A][ & A][+, reset doesn't reset the LC switches (UTAII:5-29)
// TODO: What about Saturn cards? Presumably the same as the A][ & A][+ slot0 LC?
MemResetPaging();
// For A][ & A][+, reset doesn't reset the video mode (UTAII:4-4)

View File

@ -627,7 +627,7 @@ static void OneTimeInitialization(HINSTANCE passinstance)
// Currently only support one RIFF file
if (!g_cmdLine.wavFileSpeaker.empty())
{
if (RiffInitWriteFile(g_cmdLine.wavFileSpeaker.c_str(), SPKR_SAMPLE_RATE, 1))
if (RiffInitWriteFile(g_cmdLine.wavFileSpeaker.c_str(), SPKR_SAMPLE_RATE, Spkr_GetNumChannels()))
Spkr_OutputToRiff();
}
else if (!g_cmdLine.wavFileMockingboard.empty())
@ -770,9 +770,11 @@ static void RepeatInitialization(void)
GetCardMgr().GetParallelPrinterCard()->SetEnableDumpToRealPrinter(true);
}
if (g_cmdLine.slotInsert[SLOT3] != CT_Empty && g_cmdLine.slotInsert[SLOT3] == CT_VidHD) // For now just support VidHD in slot 3
if (g_cmdLine.slotInsert[SLOT3] != CT_Empty)
{
GetCardMgr().Insert(SLOT3, g_cmdLine.slotInsert[SLOT3]);
// NB. Only support Saturn in slot 3, otherwise there's more Config UI to change
if (g_cmdLine.slotInsert[SLOT3] == CT_VidHD || g_cmdLine.slotInsert[SLOT3] == CT_Saturn128K) // For now just support VidHD and Saturn128 in slot 3)
GetCardMgr().Insert(SLOT3, g_cmdLine.slotInsert[SLOT3]);
}
if (g_cmdLine.slotInsert[SLOT4] != CT_Empty)
@ -802,6 +804,12 @@ static void RepeatInitialization(void)
{
if (GetCardMgr().QuerySlot(i) == CT_Disk2 && g_cmdLine.slotInfo[i].isDiskII13)
dynamic_cast<Disk2InterfaceCard&>(GetCardMgr().GetRef(i)).SetFirmware13Sector();
if (GetCardMgr().QuerySlot(i) == CT_GenericHDD)
{
dynamic_cast<HarddiskInterfaceCard&>(GetCardMgr().GetRef(i)).SetUserNumBlocks(g_cmdLine.uHarddiskNumBlocks);
if (g_cmdLine.useHdcFirmwareV1)
dynamic_cast<HarddiskInterfaceCard&>(GetCardMgr().GetRef(i)).UseHdcFirmwareV1();
}
}
// Create window after inserting/removing VidHD card (as it affects width & height)

View File

@ -1266,6 +1266,41 @@ int GH321_test()
//-------------------------------------
int GH1257_test()
{
const UINT kNumTests = 3;
const BYTE code[kNumTests][6] = { { 0xA2, 0x7C, 0x9A, 0x20, 0x55, 0x13 }, // LDA #$7C; TXS; JSR $1355
{ 0xA2, 0x7D, 0x9A, 0x20, 0x55, 0x13 }, // LDA #$7D; TXS; JSR $1355 - actually JSRs to $0155
{ 0xA2, 0x7E, 0x9A, 0x20, 0x55, 0x13 } }; // LDA #$7E; TXS; JSR $1355 - actually JSRs to $7D55
const WORD resPC[kNumTests] = { 0x1355, 0x0155, 0x7D55 };
const UINT org = 0x0178;
g_bStopOnBRK = true;
for (UINT i = 0; i < kNumTests; i++)
{
memcpy(mem + org, code[i], sizeof(code[i]));
mem[resPC[i]] = 0x00; // BRK
reset();
regs.pc = org;
TestCpu6502(2 + 2 + 6);
if (regs.pc != resPC[i]) return 1;
//
memcpy(mem + org, code[i], sizeof(code[i]));
mem[resPC[i]] = 0x00; // BRK
reset();
regs.pc = org;
TestCpu65C02(2 + 2 + 6);
if (regs.pc != resPC[i]) return 1;
}
return 0;
}
//-------------------------------------
int testCB(int id, int cycles, ULONG uExecutedCycles)
{
return 0;
@ -1346,6 +1381,9 @@ int _tmain(int argc, _TCHAR* argv[])
res = GH292_test();
if (res) return res;
res = GH1257_test();
if (res) return res;
res = SyncEvents_test();
if (res) return res;