mirror of
https://github.com/AppleWin/AppleWin.git
synced 2024-06-09 22:29:40 +00:00
Compare commits
38 Commits
v1.30.17.0
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
a88040c3ef | ||
|
ae7e5a63a9 | ||
|
06a646f751 | ||
|
c87a2c90da | ||
|
c0bfb0b0fe | ||
|
664d7c2d86 | ||
|
b00c9b0d3f | ||
|
d3ff855f2d | ||
|
6b4f3060c4 | ||
|
ba13412e27 | ||
|
105537e4e0 | ||
|
abfdb8e0db | ||
|
10bf60e149 | ||
|
40bf9cd2d3 | ||
|
9c1304686c | ||
|
25ce998c9a | ||
|
ff8f1b21c7 | ||
|
a85c9bfc07 | ||
|
972ab3998b | ||
|
e41478be0b | ||
|
a16a134ae5 | ||
|
0f7bec509a | ||
|
2cbecdda68 | ||
|
001f04c706 | ||
|
53fddfe41a | ||
|
fbc22fa566 | ||
|
56cc779351 | ||
|
43daabec54 | ||
|
fcd216bb45 | ||
|
d0cc5b2808 | ||
|
78ee8101d5 | ||
|
c022bbde72 | ||
|
0888a25710 | ||
|
4ab60cebf8 | ||
|
7497aa9923 | ||
|
69151a2aff | ||
|
cf0ae53018 | ||
|
fea4173b43 |
|
@ -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
|
||||
|
|
|
@ -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
11
firmware/HDDv2/build.bat
Normal 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
|
358
firmware/HDDv2/hddrvr-v2.a65
Normal file
358
firmware/HDDv2/hddrvr-v2.a65
Normal 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)
|
BIN
firmware/HDDv2/hddrvr-v2.bin
Normal file
BIN
firmware/HDDv2/hddrvr-v2.bin
Normal file
Binary file not shown.
|
@ -43,12 +43,16 @@
|
|||
-s0 <saturn|saturn64|saturn128><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 <saturn|saturn128><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 <languagecard|lc><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<N> 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 <number of ProDOS blocks><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 <number of pages><br>
|
||||
|
@ -203,9 +209,9 @@
|
|||
<br>
|
||||
-wav-speaker <file.wav><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 <file.wav><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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
BIN
resource/Apple2_Video.rom
Normal file
BIN
resource/Apple2_Video.rom
Normal file
Binary file not shown.
BIN
resource/Apple2e_Enhanced_Video.rom
Normal file
BIN
resource/Apple2e_Enhanced_Video.rom
Normal file
Binary file not shown.
|
@ -60,7 +60,6 @@ DEBUG_BUTTON BITMAP "DEBUG.BMP"
|
|||
DRIVE1_BUTTON BITMAP "DRIVE1.BMP"
|
||||
DRIVE2_BUTTON BITMAP "DRIVE2.BMP"
|
||||
SETUP_BUTTON BITMAP "SETUP.BMP"
|
||||
CHARSET40 BITMAP "CHARSET4.BMP"
|
||||
DISKOFF_BITMAP BITMAP "DISKOFF.BMP"
|
||||
DISKREAD_BITMAP BITMAP "DISKREAD.BMP"
|
||||
DISKWRITE_BITMAP BITMAP "DISKWRIT.BMP"
|
||||
|
@ -257,7 +256,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 +297,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 +331,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"
|
||||
|
@ -347,7 +347,7 @@ IDR_APPLE2_ROM ROM "Apple2.rom"
|
|||
IDR_APPLE2_PLUS_ROM ROM "Apple2_Plus.rom"
|
||||
IDR_APPLE2_JPLUS_ROM ROM "Apple2_JPlus.rom"
|
||||
IDR_APPLE2E_ROM ROM "Apple2e.rom"
|
||||
IDR_APPLE2E_ENHANCED_ROM ROM "Apple2e_Enhanced.rom"
|
||||
IDR_APPLE2E_ENHANCED_ROM ROM "Apple2e_Enhanced.rom"
|
||||
IDR_PRAVETS_82_ROM ROM "Pravets82.rom"
|
||||
IDR_PRAVETS_8M_ROM ROM "Pravets8M.rom"
|
||||
IDR_PRAVETS_8C_ROM ROM "Pravets8C.rom"
|
||||
|
@ -360,7 +360,9 @@ IDR_FREEZES_F8_ROM ROM "FREEZES_NON-AUTOSTART_F8_ROM.ro
|
|||
// VIDEO ROM
|
||||
//
|
||||
|
||||
IDR_APPLE2_VIDEO_ROM ROM "Apple2_Video.rom"
|
||||
IDR_APPLE2_JPLUS_VIDEO_ROM ROM "Apple2_JPlus_Video.rom"
|
||||
IDR_APPLE2E_ENHANCED_VIDEO_ROM ROM "Apple2e_Enhanced_Video.rom"
|
||||
IDR_BASE64A_VIDEO_ROM ROM "Base64A_German_Video.rom"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 24 KiB |
Binary file not shown.
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
BIN
resource/Hddrvr-v2.bin
Normal file
BIN
resource/Hddrvr-v2.bin
Normal file
Binary file not shown.
|
@ -28,11 +28,13 @@
|
|||
#define IDR_MENU 125
|
||||
#define IDR_APPLE2_ROM 126
|
||||
#define IDR_APPLE2_PLUS_ROM 127
|
||||
#define IDR_APPLE2E_ROM 128
|
||||
#define IDR_APPLE2E_ENHANCED_ROM 129
|
||||
#define IDD_TFE_SETTINGS_DIALOG 131
|
||||
#define IDR_PRINTDRVR_FW 132
|
||||
#define IDD_PROPPAGE_ADVANCED 132
|
||||
#define IDR_APPLE2_VIDEO_ROM 128
|
||||
#define IDR_APPLE2E_ROM 129
|
||||
#define IDR_APPLE2E_ENHANCED_ROM 130
|
||||
#define IDR_APPLE2E_ENHANCED_VIDEO_ROM 131
|
||||
#define IDD_TFE_SETTINGS_DIALOG 132
|
||||
#define IDR_PRINTDRVR_FW 133
|
||||
#define IDD_PROPPAGE_ADVANCED 133
|
||||
#define IDR_SSC_FW 134
|
||||
#define IDR_MOCKINGBOARD_D_FW 135
|
||||
#define IDR_MOUSEINTERFACE_FW 136
|
||||
|
@ -52,6 +54,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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
@ -159,6 +153,7 @@ void SY6522::Write(BYTE nReg, BYTE nValue)
|
|||
m_regs.ORB = nValue & m_regs.DDRB;
|
||||
break;
|
||||
case 0x01: // ORA
|
||||
case 0x0f: // ORA_NO_HS
|
||||
m_regs.ORA = nValue & m_regs.DDRA;
|
||||
break;
|
||||
case 0x02: // DDRB
|
||||
|
@ -226,8 +221,6 @@ void SY6522::Write(BYTE nReg, BYTE nValue)
|
|||
m_syncEvent[1]->m_canAssertIRQ = (m_regs.IER & IxR_TIMER2) ? true : false;
|
||||
UpdateIFR(0);
|
||||
break;
|
||||
case 0x0f: // ORA_NO_HS
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -351,9 +344,12 @@ 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
|
||||
case 0x0f: // IRA_NO_HS
|
||||
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;
|
||||
|
@ -404,9 +400,6 @@ BYTE SY6522::Read(BYTE nReg)
|
|||
if (m_isMegaAudio)
|
||||
nValue &= 0x7F;
|
||||
break;
|
||||
case 0x0f: // ORA_NO_HS
|
||||
nValue = m_regs.ORA;
|
||||
break;
|
||||
}
|
||||
|
||||
return nValue;
|
||||
|
|
|
@ -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(®s[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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ char FormatChar4Font(const BYTE b, bool* pWasHi_, bool* pWasLo_)
|
|||
// Disassembly
|
||||
/*
|
||||
// Thought about moving MouseText to another location, say high bit, 'A' + 0x80
|
||||
// But would like to keep compatibility with existing CHARSET40
|
||||
// But would like to keep compatibility with existing CHARSET40 - UPDATE: we now use original video ROMs (GH#1308)
|
||||
// Since we should be able to display all apple chars 0x00 .. 0xFF with minimal processing
|
||||
// Use CONSOLE_COLOR_ESCAPE_CHAR to shift to mouse text
|
||||
* Apple Font
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -402,9 +405,9 @@ bool MockingboardCard::Is6522IRQ(void)
|
|||
irq |= m_MBSubUnit[i].sy6522.GetReg(SY6522::rIFR) & 0x80 ? true : false;
|
||||
|
||||
// NB. Mockingboard generates IRQ on both 6522s:
|
||||
// . SSI263's IRQ (A/!R) is routed via the 2nd 6522 (at $Cn80) and must generate a 6502 IRQ (not NMI)
|
||||
// - NB. 2nd SSI263's IRQ is routed via the 1st 6522 (at $Cn00) and again generates a 6502 IRQ
|
||||
// . SC-01's IRQ (A/!R) is routed via the 6522 at $Cn00 (NB. Only the Mockingboard "Sound/Speech I" card supports the SC-01)
|
||||
// . SSI263's IRQ (A/!R) is routed via the 2nd 6522's CA1 input (at $Cn80) and must generate a 6502 IRQ (not NMI)
|
||||
// - NB. 2nd SSI263's IRQ is routed via the 1st 6522's CA1 input (at $Cn00) and again generates a 6502 IRQ
|
||||
// . SC-01's IRQ (!A/R) is routed via the 6522 at $Cn00 (NB. Only the Mockingboard "Sound/Speech I" card supports the SC-01)
|
||||
// Phasor's SSI263 IRQ (A/!R) line is *also* wired directly to the 6502's IRQ (as well as the 6522's CA1)
|
||||
|
||||
return irq;
|
||||
|
@ -573,7 +576,7 @@ void MockingboardCard::Reset(const bool powerCycle) // CTRL+RESET or power-cycle
|
|||
|
||||
m_MBSubUnit[subunit].Reset(QueryType());
|
||||
m_MBSubUnit[subunit].ssi263.SetCardMode(PH_Mockingboard); // Revert to PH_Mockingboard mode
|
||||
m_MBSubUnit[subunit].ssi263.Reset();
|
||||
m_MBSubUnit[subunit].ssi263.Reset(powerCycle, m_phasorEnable);
|
||||
}
|
||||
|
||||
// Reset state
|
||||
|
@ -665,7 +668,7 @@ BYTE MockingboardCard::IOReadInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE nVa
|
|||
return MemReadFloatingBus(nExecutedCycles);
|
||||
#endif
|
||||
|
||||
// NB. Mockingboard: SSI263.bit7 not readable (TODO: check this with real h/w)
|
||||
// NB. Mockingboard: SSI263.bit7 not readable
|
||||
const BYTE subunit = QueryType() == CT_SDMusic ? SY6522_DEVICE_A : !(nAddr & 0x80) ? SY6522_DEVICE_A : SY6522_DEVICE_B;
|
||||
const BYTE reg = nAddr & 0xf;
|
||||
return m_MBSubUnit[subunit].sy6522.Read(reg);
|
||||
|
@ -748,14 +751,13 @@ BYTE MockingboardCard::IOWriteInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE nV
|
|||
WriteToORB(SY6522_DEVICE_B);
|
||||
}
|
||||
|
||||
bool CS_SSI263_A = (m_phasorMode == PH_Phasor) ? !(nAddr & 0x80) && (nAddr & 0x40) // SSI263 at $Cn4x, $Cn6x
|
||||
: nAddr & 0x40; // SSI263 at $Cn4x-Cn7x, $CnCx-CnFx
|
||||
|
||||
bool CS_SSI263_B = (m_phasorMode == PH_Phasor) ? !(nAddr & 0x80) && (nAddr & 0x20) // SSI263 at $Cn2x, $Cn6x
|
||||
: nAddr & 0x20; // SSI263 at $Cn2x-Cn3x, $Cn6x-Cn7x, $CnAx-CnBx, $CnEx-CnFx
|
||||
|
||||
if (m_phasorMode == PH_Mockingboard || m_phasorMode == PH_Phasor) // No SSI263 for Echo+
|
||||
{
|
||||
// Confirmed that Phasor has no extra logic to map SSI263 (it's the same as Mockingboard's)
|
||||
bool CS_SSI263_A = nAddr & 0x40; // SSI263 at $Cn4x-Cn7x, $CnCx-CnFx
|
||||
|
||||
bool CS_SSI263_B = nAddr & 0x20; // SSI263 at $Cn2x-Cn3x, $Cn6x-Cn7x, $CnAx-CnBx, $CnEx-CnFx
|
||||
|
||||
// NB. Mockingboard mode: writes to $Cn4x/SSI263 also get written to 1st 6522 (have confirmed on real Phasor h/w)
|
||||
if (CS_SSI263_A) // Primary SSI263
|
||||
m_MBSubUnit[1].ssi263.Write(nAddr&0x7, nValue); // 2nd 6522 is used for 1st speech chip
|
||||
|
@ -836,6 +838,8 @@ BYTE MockingboardCard::PhasorIOInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE n
|
|||
m_phasorClockScaleFactor = 1;
|
||||
else if (m_phasorMode == PH_Phasor)
|
||||
m_phasorClockScaleFactor = 2;
|
||||
else // undefined mode
|
||||
m_phasorClockScaleFactor = 1; // TODO: Check for undefined Phasor mode
|
||||
|
||||
if (m_phasorMode == PH_Mockingboard)
|
||||
{
|
||||
|
@ -846,7 +850,7 @@ BYTE MockingboardCard::PhasorIOInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE n
|
|||
AY8910_InitClock((int)(Get6502BaseClock() * m_phasorClockScaleFactor));
|
||||
|
||||
for (UINT i = 0; i < NUM_SSI263; i++)
|
||||
m_MBSubUnit[i].ssi263.SetCardMode(m_phasorMode);
|
||||
m_MBSubUnit[i].ssi263.SetCardMode(m_phasorMode); // TODO: Check for undefined Phasor mode
|
||||
|
||||
#if DBG_SUPPORT_ECHOPLUS
|
||||
if (m_phasorMode == PH_EchoPlus && (nAddr & 0xf) == 0)
|
||||
|
@ -1151,7 +1155,10 @@ 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"
|
||||
//12: Added: SSI263: SC01 phoneme & active
|
||||
// Current Mode changed (added bit5 = enableInts)
|
||||
const UINT kUNIT_VERSION = 12;
|
||||
|
||||
#define SS_YAML_KEY_MB_UNIT "Unit"
|
||||
#define SS_YAML_KEY_AY_CURR_REG "AY Current Register"
|
||||
|
@ -1167,6 +1174,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
|
||||
|
@ -1226,11 +1234,14 @@ void MockingboardCard::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
|
|||
pMB->sy6522.SaveSnapshot(yamlSaveHelper);
|
||||
AY8910_SaveSnapshot(yamlSaveHelper, subunit, AY8913_DEVICE_A, std::string(""));
|
||||
pMB->ssi263.SaveSnapshot(yamlSaveHelper);
|
||||
if (subunit == 0) // has SC01
|
||||
pMB->ssi263.SC01_SaveSnapshot(yamlSaveHelper);
|
||||
|
||||
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE, pMB->state[0]);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1265,6 +1276,8 @@ bool MockingboardCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version
|
|||
UpdateIFRandIRQ(pMB, 0, pMB->sy6522.GetReg(SY6522::rIFR)); // Assert any pending IRQs (GH#677)
|
||||
AY8910_LoadSnapshot(yamlLoadHelper, subunit, AY8913_DEVICE_A, std::string(""));
|
||||
pMB->ssi263.LoadSnapshot(yamlLoadHelper, PH_Mockingboard, version); // Pre: SetVotraxPhoneme()
|
||||
if (subunit == 0) // has SC01
|
||||
pMB->ssi263.SC01_LoadSnapshot(yamlLoadHelper, version);
|
||||
|
||||
pMB->nAYCurrentRegister[0] = yamlLoadHelper.LoadUint(SS_YAML_KEY_AY_CURR_REG);
|
||||
|
||||
|
@ -1297,6 +1310,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();
|
||||
}
|
||||
|
||||
|
@ -1326,6 +1344,8 @@ void MockingboardCard::Phasor_SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
|
|||
AY8910_SaveSnapshot(yamlSaveHelper, subunit, AY8913_DEVICE_A, std::string("-A"));
|
||||
AY8910_SaveSnapshot(yamlSaveHelper, subunit, AY8913_DEVICE_B, std::string("-B"));
|
||||
pMB->ssi263.SaveSnapshot(yamlSaveHelper);
|
||||
if (subunit == 0) // has SC01
|
||||
pMB->ssi263.SC01_SaveSnapshot(yamlSaveHelper);
|
||||
|
||||
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE, pMB->state[0]);
|
||||
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE_B, pMB->state[1]);
|
||||
|
@ -1336,6 +1356,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1392,6 +1414,8 @@ bool MockingboardCard::Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT
|
|||
AY8910_LoadSnapshot(yamlLoadHelper, subunit, AY8913_DEVICE_B, std::string("-B"));
|
||||
}
|
||||
pMB->ssi263.LoadSnapshot(yamlLoadHelper, m_phasorMode, version); // Pre: SetVotraxPhoneme()
|
||||
if (subunit == 0) // has SC01
|
||||
pMB->ssi263.SC01_LoadSnapshot(yamlLoadHelper, version);
|
||||
|
||||
pMB->nAYCurrentRegister[0] = yamlLoadHelper.LoadUint(SS_YAML_KEY_AY_CURR_REG);
|
||||
if (version >= 10)
|
||||
|
@ -1430,6 +1454,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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -246,6 +246,15 @@ static void userVideoRomForIIPlus(void)
|
|||
|
||||
//-------------------------------------
|
||||
|
||||
static void VideoRomForIIandIIPlus(void)
|
||||
{
|
||||
BYTE* pVideoRom = GetFrame().GetResource(IDR_APPLE2_VIDEO_ROM, "ROM", Video::kVideoRomSize2K);
|
||||
if (pVideoRom == NULL)
|
||||
return;
|
||||
|
||||
userVideoRom2K(&csbits_a2[0], pVideoRom);
|
||||
}
|
||||
|
||||
static void VideoRomForIIJPlus(void)
|
||||
{
|
||||
BYTE* pVideoRom = GetFrame().GetResource(IDR_APPLE2_JPLUS_VIDEO_ROM, "ROM", Video::kVideoRomSize2K);
|
||||
|
@ -266,19 +275,27 @@ static void VideoRomForBase64A(void)
|
|||
userVideoRom2K(&csbits_base64a[1], pVideoRom + Video::kVideoRomSize2K, A2TYPE_BASE64A, 0);
|
||||
}
|
||||
|
||||
static void VideoRomForIIeEnhanced(void)
|
||||
{
|
||||
BYTE* pVideoRom = GetFrame().GetResource(IDR_APPLE2E_ENHANCED_VIDEO_ROM, "ROM", Video::kVideoRomSize4K);
|
||||
if (pVideoRom == NULL)
|
||||
return;
|
||||
|
||||
userVideoRom4K(&csbits_enhanced2e[0], pVideoRom);
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
void make_csbits(void)
|
||||
{
|
||||
get_csbits(&csbits_enhanced2e[0], TEXT("CHARSET40"), 0); // Enhanced //e: Alt char set off
|
||||
get_csbits(&csbits_enhanced2e[1], TEXT("CHARSET40"), 16); // Enhanced //e: Alt char set on (mousetext)
|
||||
get_csbits(&csbits_a2[0], TEXT("CHARSET40"), 32); // Apple ][, ][+
|
||||
get_csbits(&csbits_pravets82[0], TEXT("CHARSET82"), 0); // Pravets 82
|
||||
get_csbits(&csbits_pravets8M[0], TEXT("CHARSET8M"), 0); // Pravets 8M
|
||||
get_csbits(&csbits_pravets8C[0], TEXT("CHARSET8C"), 0); // Pravets 8A / 8C: Alt char set off
|
||||
get_csbits(&csbits_pravets8C[1], TEXT("CHARSET8C"), 16); // Pravets 8A / 8C: Alt char set on
|
||||
|
||||
VideoRomForIIandIIPlus(); // GH#1308
|
||||
VideoRomForIIeEnhanced(); // GH#1308
|
||||
|
||||
// Original //e is just Enhanced //e with the 32 mousetext chars [0x40..0x5F] replaced by the non-alt charset chars [0x40..0x5F]
|
||||
memcpy(csbits_2e, csbits_enhanced2e, sizeof(csbits_enhanced2e));
|
||||
memcpy(&csbits_2e[1][64], &csbits_2e[0][64], 32*8);
|
||||
|
|
|
@ -4,7 +4,7 @@ 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-2021, Tom Charlesworth, Michael Pohoreski, Nick Westgate
|
||||
Copyright (C) 2006-2024, Tom Charlesworth, Michael Pohoreski, Nick Westgate
|
||||
|
||||
AppleWin is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -23,7 +23,24 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||
|
||||
/* Description: SSI263 emulation
|
||||
*
|
||||
* Author: Various
|
||||
* Extra "spec" that's not obvious from the datasheet: (GH#175)
|
||||
* . Writes to regs 0,1,2 (and reg3.CTL=1) all de-assert the IRQ (and writes to reg3.CTL=0 and regs 4..7 don't) (GH#1197)
|
||||
* . A phoneme will continue playing back infinitely; unless the phoneme is changed or CTL=1.
|
||||
* . NB. if silenced (Amplitude=0) it's still playing.
|
||||
* . The IRQ is set at the end of the phoneme.
|
||||
* . If IRQ is then cleared, a new IRQ will occur when the phoneme completes again (but need to clear IRQ with a write to reg0, 1 or 2, even for Mockingboard-C).
|
||||
* . CTL=1 sets "PD" (Power Down / "standby") mode, also set at power-on.
|
||||
* . Registers can still be changed in this mode.
|
||||
* . IRQ de-asserted & D7=0.
|
||||
* . CTL=0 brings device out of "PD" mode, the mode will be set to DR1,DR0 and the phoneme P5-P0 will play.
|
||||
* . Setting mode to DR1:0 = %00 just disables A/!R (ie. disables interrupts), but otherwise retains the previous DR1:0 mode.
|
||||
* . If an IRQ was previously asserted then to set DR1:0=%00, you must go via CTL=1, which de-asserts the IRQ.
|
||||
* . Mockingboard-C: CTRL+RESET is not connected to !PD/!RST pin 18.
|
||||
* . Phasor: TODO: check with a 'scope.
|
||||
* . Phasor: with SSI263 ints disabled & reg0's DR1:0 != %00, then CTRL+RESET will cause SSI263 to enable ints & assert IRQ.
|
||||
* . it's as if the SSI263 does a CTL H->L to pick-up the new DR1:0. (Bug in SSI263? Assume it should remain in PD mode.)
|
||||
* . but if CTL=1, then CTRL+RESET has no effect.
|
||||
* . Power-on: PD=1 (so D7=0), reg4 (Filter Freq)=0xFF (other regs are seemingly random?).
|
||||
*/
|
||||
|
||||
#include "StdAfx.h"
|
||||
|
@ -54,26 +71,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||
|
||||
const DWORD SAMPLE_RATE_SSI263 = 22050;
|
||||
|
||||
// Duration/Phonome
|
||||
const BYTE DURATION_MODE_MASK = 0xC0;
|
||||
const BYTE DURATION_SHIFT = 6;
|
||||
const BYTE PHONEME_MASK = 0x3F;
|
||||
|
||||
const BYTE MODE_PHONEME_TRANSITIONED_INFLECTION = 0xC0; // IRQ active
|
||||
const BYTE MODE_PHONEME_IMMEDIATE_INFLECTION = 0x80; // IRQ active
|
||||
const BYTE MODE_FRAME_IMMEDIATE_INFLECTION = 0x40; // IRQ active
|
||||
const BYTE MODE_IRQ_DISABLED = 0x00;
|
||||
|
||||
// Rate/Inflection
|
||||
const BYTE RATE_MASK = 0xF0;
|
||||
const BYTE INFLECTION_MASK_H = 0x08; // I11
|
||||
const BYTE INFLECTION_MASK_L = 0x07; // I2..I0
|
||||
|
||||
// Ctrl/Art/Amp
|
||||
const BYTE CONTROL_MASK = 0x80;
|
||||
const BYTE ARTICULATION_MASK = 0x70;
|
||||
const BYTE AMPLITUDE_MASK = 0x0F;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if LOG_SSI263B
|
||||
|
@ -127,8 +124,9 @@ 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);
|
||||
return MemReadFloatingBus(m_currentMode.D7, nExecutedCycles);
|
||||
}
|
||||
|
||||
void SSI263::Write(BYTE nReg, BYTE nValue)
|
||||
|
@ -140,56 +138,48 @@ void SSI263::Write(BYTE nReg, BYTE nValue)
|
|||
ssiRegs[nReg] = nValue;
|
||||
#endif
|
||||
|
||||
// SSI263 datasheet is not clear, but a write to DURPHON de-asserts the IRQ and clears D7.
|
||||
// . Empirically writes to regs 0,1,2 (and reg3.CTL=1) all de-assert the IRQ (and writes to reg3.CTL=0 and regs 4..7 don't) (GH#1197)
|
||||
// NB. The same for Mockingboard as there's no automatic handshake from the 6522 (CA2 isn't connected to the SSI263). So writes to regs 0, 1 or 2 complete the "handshake".
|
||||
if (nReg <= SSI_RATEINF)
|
||||
{
|
||||
CpuIrqDeassert(IS_SPEECH);
|
||||
m_currentMode.D7 = 0;
|
||||
}
|
||||
|
||||
switch(nReg)
|
||||
{
|
||||
case SSI_DURPHON:
|
||||
#if LOG_SSI263
|
||||
if(g_fh) fprintf(g_fh, "DUR = 0x%02X, PHON = 0x%02X\n\n", nValue>>6, nValue&PHONEME_MASK);
|
||||
if (g_fh) fprintf(g_fh, "DUR = 0x%02X, PHON = 0x%02X\n\n", nValue>>6, nValue&PHONEME_MASK);
|
||||
LogOutput("DUR = %d, PHON = 0x%02X\n", nValue>>6, nValue&PHONEME_MASK);
|
||||
#endif
|
||||
#if LOG_SSI263B
|
||||
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;
|
||||
m_isVotraxPhoneme = false;
|
||||
|
||||
Play(nValue & PHONEME_MASK);
|
||||
if ((m_ctrlArtAmp & CONTROL_MASK) == 0)
|
||||
Play(m_durationPhoneme & PHONEME_MASK); // Play phoneme when *not* in power-down / standby mode
|
||||
break;
|
||||
case SSI_INFLECT:
|
||||
#if LOG_SSI263
|
||||
if(g_fh) fprintf(g_fh, "INF = 0x%02X\n", nValue);
|
||||
if (g_fh) fprintf(g_fh, "INF = 0x%02X\n", nValue);
|
||||
#endif
|
||||
m_inflection = nValue;
|
||||
break;
|
||||
|
||||
case SSI_RATEINF:
|
||||
#if LOG_SSI263
|
||||
if(g_fh) fprintf(g_fh, "RATE = 0x%02X, INF = 0x%02X\n", nValue>>4, nValue&0x0F);
|
||||
if (g_fh) fprintf(g_fh, "RATE = 0x%02X, INF = 0x%02X\n", nValue>>4, nValue&0x0F);
|
||||
#endif
|
||||
m_rateInflection = nValue;
|
||||
break;
|
||||
case SSI_CTTRAMP:
|
||||
#if LOG_SSI263
|
||||
if(g_fh) fprintf(g_fh, "CTRL = %d, ART = 0x%02X, AMP=0x%02X\n", nValue>>7, (nValue&ARTICULATION_MASK)>>4, nValue&LITUDE_MASK);
|
||||
if (g_fh) fprintf(g_fh, "CTRL = %d, ART = 0x%02X, AMP=0x%02X\n", nValue>>7, (nValue&ARTICULATION_MASK)>>4, nValue&LITUDE_MASK);
|
||||
//
|
||||
{
|
||||
bool H2L = (m_ctrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK);
|
||||
|
@ -201,23 +191,25 @@ void SSI263::Write(BYTE nReg, BYTE nValue)
|
|||
if ( ((m_ctrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK)) || ((nValue&0xF) == 0x0) ) // H->L or amp=0
|
||||
SSI_Output();
|
||||
#endif
|
||||
if((m_ctrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK)) // H->L
|
||||
if ((m_ctrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK)) // H->L
|
||||
{
|
||||
m_currentMode = m_durationPhoneme & DURATION_MODE_MASK;
|
||||
if (m_currentMode == MODE_IRQ_DISABLED)
|
||||
{
|
||||
// "Disables A/!R output only; does not change previous A/!R response" (SSI263 datasheet)
|
||||
// CpuIrqDeassert(IS_SPEECH);
|
||||
}
|
||||
// NB. Just changed from CTL=1 (power-down) - where IRQ was already de-asserted & D7=0
|
||||
// . So CTL H->L never affects IRQ or D7
|
||||
SetDeviceModeAndInts();
|
||||
|
||||
// Device out of power down / "standby" mode, so play phoneme
|
||||
m_isVotraxPhoneme = false;
|
||||
Play(m_durationPhoneme & PHONEME_MASK);
|
||||
}
|
||||
|
||||
m_ctrlArtAmp = nValue;
|
||||
|
||||
// "Setting the Control bit (CTL) to a logic one puts the device into Power Down mode..." (SSI263 datasheet)
|
||||
// . this silences the phoneme - actually "turns off the excitation sources and analog circuits"
|
||||
if (m_ctrlArtAmp & CONTROL_MASK)
|
||||
{
|
||||
// CpuIrqDeassert(IS_SPEECH);
|
||||
// m_currentMode &= ~1; // Clear SSI263's D7 pin
|
||||
CpuIrqDeassert(IS_SPEECH);
|
||||
m_currentMode.D7 = 0;
|
||||
}
|
||||
break;
|
||||
case SSI_FILFREQ: // RegAddr.b2=1 (b1 & b0 are: don't care)
|
||||
|
@ -230,6 +222,20 @@ void SSI263::Write(BYTE nReg, BYTE nValue)
|
|||
}
|
||||
}
|
||||
|
||||
void SSI263::SetDeviceModeAndInts(void)
|
||||
{
|
||||
if ((m_durationPhoneme & DURATION_MODE_MASK) != MODE_IRQ_DISABLED)
|
||||
{
|
||||
m_currentMode.function = (m_durationPhoneme & DURATION_MODE_MASK) >> DURATION_MODE_SHIFT;
|
||||
m_currentMode.enableInts = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// "Disables A/!R output only; does not change previous A/!R response" (SSI263 datasheet)
|
||||
m_currentMode.enableInts = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const BYTE SSI263::m_Votrax2SSI263[/*64*/] =
|
||||
|
@ -309,6 +315,7 @@ void SSI263::Votrax_Write(BYTE value)
|
|||
LogOutput("SC01: %02X (= SSI263: %02X)\n", value, m_Votrax2SSI263[value & PHONEME_MASK]);
|
||||
#endif
|
||||
m_isVotraxPhoneme = true;
|
||||
m_votraxPhoneme = value & PHONEME_MASK;
|
||||
|
||||
// !A/R: Acknowledge receipt of phoneme data (signal goes from high to low)
|
||||
UpdateIFR(m_device, SY6522::IxR_VOTRAX, 0);
|
||||
|
@ -316,13 +323,18 @@ void SSI263::Votrax_Write(BYTE value)
|
|||
// NB. Don't set reg0.DUR, as SC01's phoneme duration doesn't change with pitch (empirically determined from MAME's SC01 emulation)
|
||||
//m_durationPhoneme = value; // Set reg0.DUR = I1:0 (inflection or pitch)
|
||||
m_durationPhoneme = 0;
|
||||
Play(m_Votrax2SSI263[value & PHONEME_MASK]);
|
||||
Play(m_Votrax2SSI263[m_votraxPhoneme]);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SSI263::Play(unsigned int nPhoneme)
|
||||
{
|
||||
if (!SSI263SingleVoice.lpDSBvoice)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SSI263SingleVoice.bActive)
|
||||
{
|
||||
bool bRes = DSZeroVoiceBuffer(&SSI263SingleVoice, m_kDSBufferByteSize);
|
||||
|
@ -360,6 +372,7 @@ void SSI263::Play(unsigned int nPhoneme)
|
|||
m_phonemeAccurateLengthRemaining = m_phonemeLengthRemaining;
|
||||
m_phonemePlaybackAndDebugger = (g_nAppMode == MODE_STEPPING || g_nAppMode == MODE_DEBUG);
|
||||
m_phonemeCompleteByFullSpeed = false;
|
||||
m_phonemeLeadoutLength = m_phonemeLengthRemaining / 10; // Arbitrary! (TODO: determine a more accurate factor)
|
||||
|
||||
if (bPause)
|
||||
{
|
||||
|
@ -575,18 +588,22 @@ void SSI263::Update(void)
|
|||
|
||||
//-------------
|
||||
|
||||
const double amplitude = !m_isVotraxPhoneme ? (double)(m_ctrlArtAmp & AMPLITUDE_MASK) / (double)AMPLITUDE_MASK : 1.0;
|
||||
const double amplitude = m_isVotraxPhoneme ? 1.0
|
||||
: m_ctrlArtAmp & CONTROL_MASK ? 0.0 // Power-down / standby
|
||||
: m_filterFreq == FILTER_FREQ_SILENCE ? 0.0
|
||||
: (double)(m_ctrlArtAmp & AMPLITUDE_MASK) / (double)AMPLITUDE_MASK;
|
||||
|
||||
bool bSpeechIRQ = false;
|
||||
|
||||
{
|
||||
const BYTE DUR = m_durationPhoneme >> DURATION_SHIFT;
|
||||
const BYTE DUR = (m_currentMode.function == (MODE_FRAME_IMMEDIATE_INFLECTION >> DURATION_MODE_SHIFT)) ? 3 // Frame timing mode
|
||||
: m_durationPhoneme >> DURATION_MODE_SHIFT; // Phoneme timing mode
|
||||
const BYTE numSamplesToAvg = (DUR <= 1) ? 1 :
|
||||
(DUR == 2) ? 2 :
|
||||
4;
|
||||
|
||||
short* pMixBuffer = &m_mixBufferSSI263[0];
|
||||
int zeroSize = nNumSamples;
|
||||
UINT zeroSize = nNumSamples;
|
||||
|
||||
if (m_phonemeLengthRemaining && !prefillBufferOnInit)
|
||||
{
|
||||
|
@ -627,7 +644,12 @@ void SSI263::Update(void)
|
|||
}
|
||||
|
||||
if (zeroSize)
|
||||
{
|
||||
memset(pMixBuffer, 0, zeroSize * sizeof(short));
|
||||
|
||||
if (!prefillBufferOnInit)
|
||||
m_phonemeLeadoutLength -= (m_phonemeLeadoutLength > zeroSize) ? zeroSize : m_phonemeLeadoutLength;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -660,8 +682,24 @@ void SSI263::Update(void)
|
|||
{
|
||||
// NB. if m_phonemePlaybackAndDebugger==true, then "m_phonemeAccurateLengthRemaining!=0" must be true.
|
||||
// Since in UpdateAccurateLength(), (when m_phonemePlaybackAndDebugger==true) then m_phonemeAccurateLengthRemaining decs to zero.
|
||||
#if _DEBUG
|
||||
if (m_phonemePlaybackAndDebugger)
|
||||
{
|
||||
_ASSERT(m_phonemeAccurateLengthRemaining); // Check this!
|
||||
}
|
||||
#endif
|
||||
if (!m_phonemePlaybackAndDebugger /*|| m_phonemeAccurateLengthRemaining*/) // superfluous, so commented out (see above)
|
||||
{
|
||||
UpdateIRQ();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_phonemeLeadoutLength == 0)
|
||||
{
|
||||
if (!m_isVotraxPhoneme)
|
||||
Play(m_durationPhoneme & PHONEME_MASK); // Repeat this phoneme again
|
||||
else
|
||||
Play(m_Votrax2SSI263[m_votraxPhoneme]); // Votrax phoneme repeats too (tested in MAME 0.262)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -683,7 +721,7 @@ void SSI263::UpdateAccurateLength(void)
|
|||
const double nIrqFreq = g_fCurrentCLK6502 / updateInterval + 0.5; // Round-up
|
||||
const int nNumSamplesPerPeriod = (int)((double)(SAMPLE_RATE_SSI263) / nIrqFreq); // Eg. For 60Hz this is 367
|
||||
|
||||
const BYTE DUR = m_durationPhoneme >> DURATION_SHIFT;
|
||||
const BYTE DUR = m_durationPhoneme >> DURATION_MODE_SHIFT;
|
||||
|
||||
const UINT numSamples = nNumSamplesPerPeriod * (DUR+1);
|
||||
if (m_phonemeAccurateLengthRemaining > numSamples)
|
||||
|
@ -727,32 +765,37 @@ void SSI263::UpdateIRQ(void)
|
|||
// Pre: m_isVotraxPhoneme, m_cardMode, m_device
|
||||
void SSI263::SetSpeechIRQ(void)
|
||||
{
|
||||
if (!m_isVotraxPhoneme)
|
||||
if (!m_isVotraxPhoneme && (m_ctrlArtAmp & CONTROL_MASK) == 0)
|
||||
{
|
||||
// Always set SSI263's D7 pin regardless of SSI263 mode (DR1:0), including MODE_IRQ_DISABLED
|
||||
m_currentMode |= 1; // Set SSI263's D7 pin
|
||||
|
||||
if ((m_currentMode & DURATION_MODE_MASK) != MODE_IRQ_DISABLED)
|
||||
if (m_currentMode.enableInts)
|
||||
{
|
||||
if (m_cardMode == PH_Mockingboard)
|
||||
{
|
||||
if ((GetPCR(m_device) & 1) == 0) // CA1 Latch/Input = 0 (Negative active edge)
|
||||
UpdateIFR(m_device, 0, SY6522::IxR_SSI263);
|
||||
if (GetPCR(m_device) == 0x0C) // CA2 Control = b#110 (Low output)
|
||||
m_currentMode &= ~1; // Clear SSI263's D7 pin (cleared by 6522's PCR CA1/CA2 handshake)
|
||||
|
||||
// NB. Don't set CTL=1, as Mockingboard(SMS) speech doesn't work (it sets MODE_IRQ_DISABLED mode during ISR)
|
||||
//pMB->SpeechChip.CtrlArtAmp |= CONTROL_MASK; // 6522's CA2 sets Power Down mode (pin 18), which sets Control bit
|
||||
if (m_currentMode.D7 == 0)
|
||||
{
|
||||
// 6522's PCR = 0x0C (all SSI263 speech routine use this value, but 0x00 will do equally as well!)
|
||||
// . b3:1 CA2 Control = b#110 (Low output) - not connected
|
||||
// . b0 CA1 Latch/Input = 0 (Negative active edge) - input from SSI263's A/!R
|
||||
if ((GetPCR(m_device) & 1) == 0) // Level change from SSI263's A/!R, latch this as an interrupt
|
||||
UpdateIFR(m_device, 0, SY6522::IxR_SSI263);
|
||||
}
|
||||
}
|
||||
else if (m_cardMode == PH_Phasor) // Phasor's SSI263 IRQ (A/!R) line is *also* wired directly to the 6502's IRQ (as well as the 6522's CA1)
|
||||
else if (m_cardMode == PH_Phasor)
|
||||
{
|
||||
// Phasor (in native mode): SSI263 IRQ (A/!R) pin is connected directly to the 6502's IRQ
|
||||
// . And Mockingboard mode: A/!R is connected to the 6522's CA1
|
||||
CpuIrqAssert(IS_SPEECH);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ASSERT(0);
|
||||
_ASSERT(m_cardMode == PH_EchoPlus);
|
||||
// SSI263 not visible from Echo+ mode, but still continues to operate
|
||||
}
|
||||
}
|
||||
|
||||
// Always set SSI263's D7 pin regardless of SSI263 mode (DR1:0), including when SSI263 ints are disabled (via MODE_IRQ_DISABLED)
|
||||
// NB. Don't set D7 when in power-down / standby mode.
|
||||
m_currentMode.D7 = 1;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -760,15 +803,34 @@ void SSI263::SetSpeechIRQ(void)
|
|||
if (m_isVotraxPhoneme && GetPCR(m_device) == 0xB0)
|
||||
{
|
||||
// !A/R: Time-out of old phoneme (signal goes from low to high)
|
||||
|
||||
UpdateIFR(m_device, 0, SY6522::IxR_VOTRAX);
|
||||
|
||||
m_isVotraxPhoneme = false;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SSI263::SetCardMode(PHASOR_MODE mode)
|
||||
{
|
||||
const PHASOR_MODE oldCardMode = m_cardMode;
|
||||
m_cardMode = mode;
|
||||
|
||||
if (oldCardMode == m_cardMode)
|
||||
return;
|
||||
|
||||
// mode change
|
||||
|
||||
if (m_currentMode.D7 == 1)
|
||||
{
|
||||
m_currentMode.D7 = 0; // So that \PH_Mockingboard\ path sets IFR. Post: D7=1
|
||||
SetSpeechIRQ();
|
||||
}
|
||||
|
||||
if (m_cardMode != PH_Phasor)
|
||||
CpuIrqDeassert(IS_SPEECH);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SSI263::DSInit(void)
|
||||
{
|
||||
//
|
||||
|
@ -805,10 +867,27 @@ void SSI263::DSUninit(void)
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SSI263::Reset(void)
|
||||
// SSI263 phoneme continues to play after CTRL+RESET (tested on real h/w)
|
||||
// Votrax phoneme continues to play after CTRL+RESET (tested on MAME 0.262)
|
||||
void SSI263::Reset(const bool powerCycle, const bool isPhasorCard)
|
||||
{
|
||||
if (!powerCycle)
|
||||
{
|
||||
if (isPhasorCard)
|
||||
{
|
||||
// Empirically observed it does CTL H->L to enable ints (and set the device mode?) (GH#175)
|
||||
// NB. CTRL+RESET doesn't clear m_ctrlArtAmp.CTL (ie. if the device is in power-down/standby mode then ignore RST)
|
||||
// Speculate that there's a bug in the SSI263 and that RST should put the device into power-down/standby mode (ie. silence the device)
|
||||
// TODO: Stick a 'scope on !PD/!RST pin 18 to see what the Phasor h/w does.
|
||||
if ((m_ctrlArtAmp & CONTROL_MASK) == 0)
|
||||
SetDeviceModeAndInts();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Stop();
|
||||
ResetState();
|
||||
ResetState(powerCycle);
|
||||
CpuIrqDeassert(IS_SPEECH);
|
||||
}
|
||||
|
||||
|
@ -878,7 +957,7 @@ void SSI263::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
|
|||
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_REG_RATE_INF, m_rateInflection);
|
||||
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_REG_CTRL_ART_AMP, m_ctrlArtAmp);
|
||||
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_REG_FILTER_FREQ, m_filterFreq);
|
||||
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_CURRENT_MODE, m_currentMode);
|
||||
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_CURRENT_MODE, m_currentMode.mode);
|
||||
yamlSaveHelper.SaveBool(SS_YAML_KEY_SSI263_ACTIVE_PHONEME, IsPhonemeActive());
|
||||
}
|
||||
|
||||
|
@ -892,19 +971,32 @@ void SSI263::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, PHASOR_MODE mode, UINT
|
|||
m_rateInflection = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_REG_RATE_INF);
|
||||
m_ctrlArtAmp = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_REG_CTRL_ART_AMP);
|
||||
m_filterFreq = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_REG_FILTER_FREQ);
|
||||
m_currentMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_CURRENT_MODE);
|
||||
m_currentMode.mode = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_CURRENT_MODE);
|
||||
bool activePhoneme = (version >= 7) ? yamlLoadHelper.LoadBool(SS_YAML_KEY_SSI263_ACTIVE_PHONEME) : false;
|
||||
m_currentActivePhoneme = !activePhoneme ? -1 : 0x00; // Not important which phoneme, since UpdateIRQ() resets this
|
||||
|
||||
if (version < 12)
|
||||
{
|
||||
if (m_currentMode.function == 0) // invalid function (but in older versions this was accepted)
|
||||
{
|
||||
m_currentMode.function = MODE_PHONEME_TRANSITIONED_INFLECTION >> DURATION_MODE_SHIFT; // Typically this is used
|
||||
m_currentMode.enableInts = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentMode.enableInts = 1;
|
||||
}
|
||||
}
|
||||
|
||||
yamlLoadHelper.PopMap();
|
||||
|
||||
//
|
||||
|
||||
_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)
|
||||
if (m_cardMode == PH_Phasor && (m_currentMode & DURATION_MODE_MASK) != MODE_IRQ_DISABLED && (m_currentMode & 1))
|
||||
if (m_cardMode == PH_Phasor && (m_ctrlArtAmp & CONTROL_MASK) == 0 && m_currentMode.enableInts && m_currentMode.D7 == 1)
|
||||
CpuIrqAssert(IS_SPEECH);
|
||||
|
||||
if (IsPhonemeActive())
|
||||
|
@ -912,3 +1004,30 @@ void SSI263::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, PHASOR_MODE mode, UINT
|
|||
|
||||
m_lastUpdateCycle = GetLastCumulativeCycles();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
#define SS_YAML_KEY_SC01 "SC01"
|
||||
// NB. No version - this is determined by the parent "Mockingboard C" or "Phasor" unit
|
||||
|
||||
#define SS_YAML_KEY_SC01_PHONEME "SC01 Phoneme"
|
||||
#define SS_YAML_KEY_SC01_ACTIVE_PHONEME "SC01 Active Phoneme"
|
||||
|
||||
void SSI263::SC01_SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
|
||||
{
|
||||
YamlSaveHelper::Label label(yamlSaveHelper, "%s:\n", SS_YAML_KEY_SC01);
|
||||
|
||||
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SC01_PHONEME, m_votraxPhoneme);
|
||||
yamlSaveHelper.SaveBool(SS_YAML_KEY_SC01_ACTIVE_PHONEME, m_isVotraxPhoneme);
|
||||
}
|
||||
|
||||
void SSI263::SC01_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
|
||||
{
|
||||
if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_SC01))
|
||||
throw std::runtime_error("Card: Expected key: " SS_YAML_KEY_SC01);
|
||||
|
||||
m_votraxPhoneme = yamlLoadHelper.LoadUint(SS_YAML_KEY_SC01_PHONEME);
|
||||
m_isVotraxPhoneme = yamlLoadHelper.LoadBool(SS_YAML_KEY_SC01_ACTIVE_PHONEME);
|
||||
|
||||
yamlLoadHelper.PopMap();
|
||||
}
|
||||
|
|
|
@ -11,17 +11,18 @@ public:
|
|||
m_cardMode = PH_Mockingboard;
|
||||
m_pPhonemeData00 = NULL;
|
||||
|
||||
ResetState();
|
||||
ResetState(true);
|
||||
}
|
||||
~SSI263(void)
|
||||
{
|
||||
delete [] m_pPhonemeData00;
|
||||
}
|
||||
|
||||
void ResetState(void)
|
||||
void ResetState(const bool powerCycle)
|
||||
{
|
||||
m_currentActivePhoneme = -1;
|
||||
m_isVotraxPhoneme = false;
|
||||
m_votraxPhoneme = 0;
|
||||
m_cyclesThisAudioFrame = 0;
|
||||
|
||||
//
|
||||
|
@ -34,6 +35,7 @@ public:
|
|||
m_phonemeAccurateLengthRemaining = 0;
|
||||
m_phonemePlaybackAndDebugger = false;
|
||||
m_phonemeCompleteByFullSpeed = false;
|
||||
m_phonemeLeadoutLength = 0;
|
||||
|
||||
//
|
||||
|
||||
|
@ -45,13 +47,19 @@ public:
|
|||
|
||||
//
|
||||
|
||||
m_durationPhoneme = 0;
|
||||
// After a chip power-on, if the first thing done is CTL=0, then empirically it can be observed that:
|
||||
// . enableInts = 1 (mostly an SSI263 interrupt occurs, but not always)
|
||||
// . DR1:0 != b#00 (since enableInts is set to 1, except when no interrupt => DR1:0 == b#00)
|
||||
m_durationPhoneme = MODE_PHONEME_TRANSITIONED_INFLECTION; // Typical function & phoneme=$00
|
||||
m_inflection = 0;
|
||||
m_rateInflection = 0;
|
||||
m_ctrlArtAmp = 0;
|
||||
m_filterFreq = 0;
|
||||
m_ctrlArtAmp = powerCycle ? CONTROL_MASK : 0; // Chip power-on, so CTL=1 (power-down / standby)
|
||||
m_filterFreq = powerCycle ? FILTER_FREQ_SILENCE : 0; // Empirically observed at chip power-on (GH#1302)
|
||||
|
||||
m_currentMode = 0;
|
||||
m_currentMode.mode = 0;
|
||||
m_currentMode.function = 0; // Set at runtime when CTL=0
|
||||
m_currentMode.enableInts = 0; // Set at runtime when CTL=0
|
||||
m_currentMode.D7 = 0; // Since in power-down mode (as per SSI263 datasheet)
|
||||
|
||||
//
|
||||
|
||||
|
@ -60,12 +68,12 @@ public:
|
|||
}
|
||||
|
||||
void SetDevice(UINT device) { m_device = device; }
|
||||
void SetCardMode(PHASOR_MODE mode) { m_cardMode = mode; }
|
||||
void SetCardMode(PHASOR_MODE mode);
|
||||
|
||||
bool DSInit(void);
|
||||
void DSUninit(void);
|
||||
|
||||
void Reset(void);
|
||||
void Reset(const bool powerCycle, const bool isPhasorCard);
|
||||
bool IsPhonemeActive(void) { return m_currentActivePhoneme >= 0; }
|
||||
|
||||
BYTE Read(ULONG nExecutedCycles);
|
||||
|
@ -85,12 +93,15 @@ public:
|
|||
|
||||
void SaveSnapshot(class YamlSaveHelper& yamlSaveHelper);
|
||||
void LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, PHASOR_MODE mode, UINT version);
|
||||
void SC01_SaveSnapshot(YamlSaveHelper& yamlSaveHelper);
|
||||
void SC01_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version);
|
||||
|
||||
private:
|
||||
void Play(unsigned int nPhoneme);
|
||||
void Stop(void);
|
||||
void UpdateIRQ(void);
|
||||
void UpdateAccurateLength(void);
|
||||
void SetDeviceModeAndInts(void);
|
||||
|
||||
UINT64 GetLastCumulativeCycles(void);
|
||||
void UpdateIFR(BYTE nDevice, BYTE clr_mask, BYTE set_mask);
|
||||
|
@ -105,13 +116,37 @@ private:
|
|||
|
||||
//
|
||||
|
||||
// Duration/Phonome
|
||||
static const BYTE DURATION_MODE_MASK = 0xC0;
|
||||
static const BYTE DURATION_MODE_SHIFT = 6;
|
||||
static const BYTE PHONEME_MASK = 0x3F;
|
||||
|
||||
static const BYTE MODE_PHONEME_TRANSITIONED_INFLECTION = 0xC0;
|
||||
static const BYTE MODE_PHONEME_IMMEDIATE_INFLECTION = 0x80;
|
||||
static const BYTE MODE_FRAME_IMMEDIATE_INFLECTION = 0x40;
|
||||
static const BYTE MODE_IRQ_DISABLED = 0x00; // disable interrupts, but retains one of the 3 modes
|
||||
|
||||
// Rate/Inflection
|
||||
static const BYTE RATE_MASK = 0xF0;
|
||||
static const BYTE INFLECTION_MASK_H = 0x08; // I11
|
||||
static const BYTE INFLECTION_MASK_L = 0x07; // I2..I0
|
||||
|
||||
// Ctrl/Art/Amp
|
||||
static const BYTE ARTICULATION_MASK = 0x70;
|
||||
static const BYTE AMPLITUDE_MASK = 0x0F;
|
||||
static const BYTE CONTROL_MASK = 0x80;
|
||||
|
||||
// Filter frequency range
|
||||
static const BYTE FILTER_FREQ_SILENCE = 0xFF;
|
||||
|
||||
UINT m_slot;
|
||||
BYTE m_device; // SSI263 device# which is generating phoneme-complete IRQ (and only required whilst Mockingboard isn't a class)
|
||||
PHASOR_MODE m_cardMode;
|
||||
short* m_pPhonemeData00;
|
||||
|
||||
int m_currentActivePhoneme;
|
||||
int m_currentActivePhoneme; // -1 (if none) or SSI263 or SC01 phoneme
|
||||
bool m_isVotraxPhoneme;
|
||||
BYTE m_votraxPhoneme;
|
||||
UINT m_cyclesThisAudioFrame;
|
||||
|
||||
//
|
||||
|
@ -124,6 +159,7 @@ private:
|
|||
UINT m_phonemeAccurateLengthRemaining; // length in samples, decremented by cycles executed
|
||||
bool m_phonemePlaybackAndDebugger;
|
||||
bool m_phonemeCompleteByFullSpeed;
|
||||
UINT m_phonemeLeadoutLength; // length in samples, decremented after \m_phonemeLengthRemaining\ goes to zero. Delay until phoneme repeats
|
||||
|
||||
//
|
||||
|
||||
|
@ -140,7 +176,17 @@ private:
|
|||
BYTE m_ctrlArtAmp;
|
||||
BYTE m_filterFreq;
|
||||
|
||||
BYTE m_currentMode; // b7:6=Mode; b0=D7 pin (for IRQ)
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
BYTE D7 : 1; // b0=D7 pin (for IRQ)
|
||||
BYTE reserved : 4;
|
||||
BYTE enableInts : 1; // b5 = enable A/!R (ie. interrupts)
|
||||
BYTE function : 2; // b7:6 = function
|
||||
};
|
||||
BYTE mode;
|
||||
} m_currentMode;
|
||||
|
||||
// Debug
|
||||
bool m_dbgFirst;
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
//
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||
|
||||
#ifndef _MSC_VER
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
|
|
|
@ -69,22 +69,16 @@ typedef int socklen_t;
|
|||
#define SOCK_EWOULDBLOCK EWOULDBLOCK
|
||||
#define SOCK_EINPROGRESS EINPROGRESS
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.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 +906,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 +920,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user