WIP: Replace VIAEMDEV.c with rewrite

This commit is contained in:
InvisibleUp 2020-07-11 17:14:53 -04:00
parent 4264ff2de5
commit 02b8c9c721
8 changed files with 706 additions and 2891 deletions

View File

@ -1,300 +0,0 @@
/*
VIAEMDEV.c
Copyright (C) 2020 InvisibleUp
You can redistribute this file and/or modify it under the terms
of version 2 of the GNU General Public License as published by
the Free Software Foundation. You should have received a copy
of the license along with this file; see the file COPYING.
This file 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
license for more details.
*/
/*
Versatile Interface Adapter EMulated DEVice
Emulates the Synertek SY6522 VIA found up until the Mac Plus.
This code rewritten for target-independance and non-enum based config
The VIA1 contains the following functionality:
- Two timers (1 and 2)
- Serial-to-parallel / parallel-to-serial bi-directional shift register
- Two 8-bit bi-directional ports (A and B)
- Ports are per-line settable to input or output
- Square wave generation
- Pulse counting
The M68000 addresses the VIA simply via an interrupt line and a pair of
registers, much like any other peripheal
Because just about everything on the Mac is attached to a VIA, it's
important to track what device is mapped to what line on what port.
Summary of SY6522 pins:
1. Phase 2 Clock (Φ2)
Must be high for data transfer from M68k to occur
Time base for timers, shift registers, etc.
2. Chip select (CS1, ¬CS2)
is a chip select line
3. Register Select (RS0, RS1, RS2, RS3)
Name Write Read
0000 - ORB, IRB Port B output Port B input
0001 - ORA, IRA Port A output Port A input
0010 - DDRB Data Direction, port B
0011 - DDRA Data Direction, port A
0100 - T1L-L T1 Low-Order Latches T1 Low-Order Counter
0101 - T1C-H T1 High-Order Counter
0110 - T1L-L T1 Low-Order Latches
0111 - T1L-H T1 High-Order Latches
1000 - T2C-L T2 Low-Order Latches T2 Low-Order Counter
1001 - T2C-H T2 High-Order Counter
1010 - SR Shift Register
1011 - ACR Auxillary Control Register
1100 - PCR Peripheal Control Register
1101 - IFR Interrupt flag
1110 - IER Interrupt enable
1111 - ORA Same as 0001 but no effect on handshake
4. Read/Write line
If low, write (processor to VIA register)
If high, read (VIA register to processor)
5. Data Bus (DB0-7)
Transfers data between VIA and M68l
6. Reset
Clears all registers to 0.
All I/O set to input, disablse timers, SR, interrupts, etc.
7. IRQ
8. Peripheal A Port (PA0 - PA7)
8 lines. Each can be input or output, depending on value of DDRA
9. Port A Control Lines (CA1/CA2)
Interrupt inputs or handshake outputs
Each line controls an internal interrupt flag w/ enable bit
CA1 controls latching of data on P1 input
10. Peripheal Port B (PB0 - PB7)
Same as Port A, with timers!
PB7 polarity can be controller by timer
PB6 can count pulses w/ second timer
11. Port B control lines (CB1/CB2)
Same as CA
Shift register serial I/O goes through one of these
I'm not going to bother to emulate handshaking
*/
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "VIAEMDEV.h"
/* Global state */
typedef struct {
uint8_t PortA_data; // Port A data
uint8_t PortA_dir; // Direction of Port A bits (0 = in, 1 = out)
uint8_t PortB_data; // Port A data
uint8_t PortB_dir; // Direction of Port B bits (0 = in, 1 = out)
uint8_t SR; // current shift register state
uint16_t T1_C; // timer 1 count
uint16_t T1_L; // timer 1 latches
uint16_t T2_C; // timer 2 count
uint16_t T2_L; // timer 2 latches
uint8_t AUX_T1; // Aux - Timer 1 Control (0-3)
uint8_t AUX_T2; // Aux - Timer 2 Control (0-1)
uint8_t AUX_SR; // Aux - SR Control (0-7)
bool AUX_PALE; // Aux - Port A Latch Enable (0-1)
bool AUX_PBLE; // Aux - Port B Latch Enable (0-1)
uint8_t PC_CB2; // Perip. - CB2 Control
uint8_t PC_CB1; // Perip. - CB1 Edge Polarity (0 = -, 1 = +)
uint8_t PC_CA2; // Perip. - CA2 Control
uint8_t PC_CA1; // Perip. - CA1 Edge Polarity (0 = -, 1 = +)
uint8_t IF; // Interrupt Flags
uint8_t IE; // Interrupt Enable
} VIA1_State_t;
VIA1_State_t VIA1_State = {0};
/*
For reference: Mac 128/512k port allocations:
PA7 (I ) SCC ready for read
PA6 ( O) Alt. screen buffer in use?
PA5 ( O) Disk SEL line
PA4 ( O) ROM low-mem overlay
PA3 ( O) Alt. sound buffer
PA0-2 ( O) Sound volume
PB7 ( O) Sound on/off
PB6 (I ) Horiz. Blank
PB5 (I ) Mouse Y2
PB4 (I ) Mouse X2
PB3 (I ) Mouse button
PB2 ( O) RTC serial enable
RB1 ( O) RTC data-clock line
RB0 (IO) RTC clock serial data
SR - Keyboard data line
Timer 1 - Sound generator stuff
Timer 2 - Disk I/O events
(or both can be for your own use!)
IRQ7 - IRQ (all enabled VIA interrupts)
IRQ6 - Timer 1
IRQ5 - Timer 2
IRQ4 - Keyboard clock
IRQ3 - Keyboard data bit
IRQ2 - Keyboard data ready
IRQ1 - VBlank
IRQ0 - One-second interrupt from RTC
*/
// Hardware reset
void VIA1_Zap(void) {
memset(&VIA1_State, 0, sizeof(VIA1_State));
}
// Software reset
void VIA1_Reset(void) {
VIA1_Zap();
}
// Write to a register
void VIA1_Write(uint8_t reg, uint8_t data)
{
switch(reg) {
case 0: // Port B data
VIA1_State.PortB_data = data;
break;
case 1: // Port A data
case 15:
VIA1_State.PortA_data = data;
break;
case 2: // Port B direction
VIA1_State.PortB_dir = data;
break;
case 3: // Port A direction
VIA1_State.PortA_dir = data;
break;
case 4: // Timer 1 Low-Order Counter
case 6: // Timer 1 Low-Order Latches
VIA1_State.T1_L &= 0xFF00 | data;
break;
case 5: // Timer 1 High-Order Counter
VIA1_State.T1_L &= 0x00FF | (data << 8);
VIA1_State.T1_C = VIA1_State.T1_L;
VIA1_State.IF &= 0b10111111;
break;
case 7: // Timer 1 High-Order Latches
VIA1_State.T1_L &= 0x00FF | (data << 8);
break;
case 8: // Timer 1 Low-Order Counter
VIA1_State.T2_L &= 0xFF00 | data;
break;
case 9: // Timer 2 High-Order Counter
VIA1_State.T2_L &= 0x00FF | (data << 8);
VIA1_State.T2_C = VIA1_State.T2_L;
VIA1_State.IF &= 0b10111111;
break;
case 10:
VIA1_State.SR = data;
break;
case 11:
VIA1_State.AUX_T1 = (data & 0b11000000) >> 6;
VIA1_State.AUX_T2 = (data & 0b00100000) >> 5;
VIA1_State.AUX_SR = (data & 0b00011100) >> 2;
VIA1_State.AUX_PBLE = (data & 0b00000010) >> 1;
VIA1_State.AUX_PALE = (data & 0b00000001) >> 0;
break;
case 12:
VIA1_State.PC_CB2 = (data & 0b11100000) >> 5;
VIA1_State.PC_CB1 = (data & 0b00010000) >> 4;
VIA1_State.PC_CA2 = (data & 0b00001110) >> 1;
VIA1_State.PC_CA1 = (data & 0b00000001) >> 0;
break;
case 13: // Interrupt Flag
VIA1_State.IF = data;
break;
case 14: // Interrupt Enable
VIA1_State.IE = data;
break;
}
}
// Read to a register
uint8_t VIA1_Read(uint8_t reg)
{
switch(reg) {
case 0: // Port B data (technically incorrect but meh)
return VIA1_State.PortB_data & ~VIA1_State.PortB_dir;
case 1: // Port A data
case 15:
return VIA1_State.PortA_data & ~VIA1_State.PortA_dir;
case 2: // Port B direction
return VIA1_State.PortB_dir;
case 3: // Port A direction
return VIA1_State.PortA_dir;
case 4: // Timer 1 Low-Order Counter
VIA1_State.IF &= 0b10111111;
return (VIA1_State.T1_C & 0xFF00);
case 5: // Timer 1 High-Order Counter
return (VIA1_State.T1_C & 0x00FF) >> 8;
case 6: // Timer 1 Low-Order Latches
return (VIA1_State.T1_L & 0xFF00);
case 7: // Timer 1 High-Order Latches
return (VIA1_State.T1_L & 0x00FF) >> 8;
case 8: // Timer 2 Low-Order Counter
VIA1_State.IF &= 0b11011111;
return (VIA1_State.T2_C & 0xFF00);
case 9: // Timer 2 High-Order Counter
return (VIA1_State.T2_C & 0x00FF) >> 8;
case 10:
return VIA1_State.SR;
case 11:
return (
(VIA1_State.AUX_T1 << 6) |
(VIA1_State.AUX_T2 << 5) |
(VIA1_State.AUX_SR << 2) |
(VIA1_State.AUX_PBLE << 1) |
(VIA1_State.AUX_PALE << 0)
);
case 12:
return (
(VIA1_State.PC_CB2 << 5) |
(VIA1_State.PC_CB1 << 4) |
(VIA1_State.PC_CA2 << 1) |
(VIA1_State.PC_CA1 << 0)
);
case 13: // Interrupt Flag
return VIA1_State.IF;
break;
case 14: // Interrupt Enable
return VIA1_State.IE;
break;
default:
return 0;
}
}
// Tick timers
void VIA1_Tick() {
}
// Shift in one byte of data to keyboard shift register
void VIA1_ShiftInData(uint8_t v)
{
VIA1_State.SR = v;
// somehow signal to keyboard that we're ready
}
// Shift out one byte of data from keyboard shift register
uint8_t VIA1_ShiftOutData(void)
{
// signal to keyboard to get new data?
return VIA1_State.SR;
}

View File

@ -27,119 +27,6 @@
#define kRomCheckSum3 0x4D1F8172
#define kROM_Size 0x00020000
/* the Wire variables are 1/0, not true/false */
enum {
Wire_VIA1_iA0_SoundVolb0,
#define SoundVolb0 (Wires[Wire_VIA1_iA0_SoundVolb0])
#define VIA1_iA0 (Wires[Wire_VIA1_iA0_SoundVolb0])
Wire_VIA1_iA1_SoundVolb1,
#define SoundVolb1 (Wires[Wire_VIA1_iA1_SoundVolb1])
#define VIA1_iA1 (Wires[Wire_VIA1_iA1_SoundVolb1])
Wire_VIA1_iA2_SoundVolb2,
#define SoundVolb2 (Wires[Wire_VIA1_iA2_SoundVolb2])
#define VIA1_iA2 (Wires[Wire_VIA1_iA2_SoundVolb2])
Wire_VIA1_iA4_MemOverlay,
#define MemOverlay (Wires[Wire_VIA1_iA4_MemOverlay])
#define VIA1_iA4 (Wires[Wire_VIA1_iA4_MemOverlay])
#define VIA1_iA4_ChangeNtfy MemOverlay_ChangeNtfy
Wire_VIA1_iA6_SCRNvPage2,
#define SCRNvPage2 (Wires[Wire_VIA1_iA6_SCRNvPage2])
#define VIA1_iA6 (Wires[Wire_VIA1_iA6_SCRNvPage2])
Wire_VIA1_iA5_IWMvSel,
#define IWMvSel (Wires[Wire_VIA1_iA5_IWMvSel])
#define VIA1_iA5 (Wires[Wire_VIA1_iA5_IWMvSel])
Wire_VIA1_iA7_SCCwaitrq,
#define SCCwaitrq (Wires[Wire_VIA1_iA7_SCCwaitrq])
#define VIA1_iA7 (Wires[Wire_VIA1_iA7_SCCwaitrq])
Wire_VIA1_iB0_RTCdataLine,
#define RTCdataLine (Wires[Wire_VIA1_iB0_RTCdataLine])
#define VIA1_iB0 (Wires[Wire_VIA1_iB0_RTCdataLine])
#define VIA1_iB0_ChangeNtfy RTCdataLine_ChangeNtfy
Wire_VIA1_iB1_RTCclock,
#define RTCclock (Wires[Wire_VIA1_iB1_RTCclock])
#define VIA1_iB1 (Wires[Wire_VIA1_iB1_RTCclock])
#define VIA1_iB1_ChangeNtfy RTCclock_ChangeNtfy
Wire_VIA1_iB2_RTCunEnabled,
#define RTCunEnabled (Wires[Wire_VIA1_iB2_RTCunEnabled])
#define VIA1_iB2 (Wires[Wire_VIA1_iB2_RTCunEnabled])
#define VIA1_iB2_ChangeNtfy RTCunEnabled_ChangeNtfy
Wire_VIA1_iA3_SoundBuffer,
#define SoundBuffer (Wires[Wire_VIA1_iA3_SoundBuffer])
#define VIA1_iA3 (Wires[Wire_VIA1_iA3_SoundBuffer])
Wire_VIA1_iB3_MouseBtnUp,
#define MouseBtnUp (Wires[Wire_VIA1_iB3_MouseBtnUp])
#define VIA1_iB3 (Wires[Wire_VIA1_iB3_MouseBtnUp])
Wire_VIA1_iB4_MouseX2,
#define MouseX2 (Wires[Wire_VIA1_iB4_MouseX2])
#define VIA1_iB4 (Wires[Wire_VIA1_iB4_MouseX2])
Wire_VIA1_iB5_MouseY2,
#define MouseY2 (Wires[Wire_VIA1_iB5_MouseY2])
#define VIA1_iB5 (Wires[Wire_VIA1_iB5_MouseY2])
Wire_VIA1_iCB2_KybdDat,
#define VIA1_iCB2 (Wires[Wire_VIA1_iCB2_KybdDat])
#define VIA1_iCB2_ChangeNtfy Kybd_DataLineChngNtfy
Wire_VIA1_iB6_SCRNbeamInVid,
#define SCRNbeamInVid (Wires[Wire_VIA1_iB6_SCRNbeamInVid])
#define VIA1_iB6 (Wires[Wire_VIA1_iB6_SCRNbeamInVid])
Wire_VIA1_iB7_SoundDisable,
#define SoundDisable (Wires[Wire_VIA1_iB7_SoundDisable])
#define VIA1_iB7 (Wires[Wire_VIA1_iB7_SoundDisable])
Wire_VIA1_InterruptRequest,
#define VIA1_InterruptRequest (Wires[Wire_VIA1_InterruptRequest])
#define VIA1_interruptChngNtfy VIAorSCCinterruptChngNtfy
Wire_SCCInterruptRequest,
#define SCCInterruptRequest (Wires[Wire_SCCInterruptRequest])
#define SCCinterruptChngNtfy VIAorSCCinterruptChngNtfy
kNumWires
};
/* VIA configuration */
#define VIA1_ORA_FloatVal 0xFF
#define VIA1_ORB_FloatVal 0xFF
#define VIA1_ORA_CanIn 0x80
#define VIA1_ORA_CanOut 0x7F
#define VIA1_ORB_CanIn 0x79
#define VIA1_ORB_CanOut 0x87
#define VIA1_IER_Never0 (1 << 1)
#define VIA1_IER_Never1 ((1 << 3) | (1 << 4))
#define VIA1_CB2modesAllowed 0x01
#define VIA1_CA2modesAllowed 0x01
#define Mouse_Enabled SCC_InterruptsEnabled
#define VIA1_iCA1_PulseNtfy VIA1_iCA1_Sixtieth_PulseNtfy
#define Sixtieth_PulseNtfy VIA1_iCA1_Sixtieth_PulseNtfy
#define VIA1_iCA2_PulseNtfy VIA1_iCA2_RTC_OneSecond_PulseNtfy
#define RTC_OneSecond_PulseNtfy VIA1_iCA2_RTC_OneSecond_PulseNtfy
#define GetSoundInvertTime VIA1_GetT1InvertTime
#define KYBD_ShiftInData VIA1_ShiftOutData
#define KYBD_ShiftOutData VIA1_ShiftInData
#define kExtn_Block_Base 0x00F40000
#define kExtn_ln2Spc 5

471
src/HW/VIA/VIA notes.md Normal file
View File

@ -0,0 +1,471 @@
# VIA
Emulates the Synertek SY6522 VIA in every system that uvMac targets.
The VIA contains the following functionality:
- Two timers (1 and 2)
- Serial-to-parallel / parallel-to-serial bi-directional shift register
- Two 8-bit bi-directional ports (A and B)
- Ports are per-line settable to input or output
- Square wave generation
- Pulse counting
The M68000 addresses the VIA simply via an interrupt line and a pair of
registers, much like any other peripheal
Because just about everything on the Mac is attached to a VIA, it's
important to track what device is mapped to what line on what port.
## Summary of SY6522 pins:
1. Phase 2 Clock (Φ2)
Must be high for data transfer from M68k to occur
Time base for timers, shift registers, etc.
2. Chip select (CS1, ¬CS2)
is a chip select line
3. Register Select (RS0, RS1, RS2, RS3)
Name Write Read
0000 - ORB, IRB Port B output Port B input
0001 - ORA, IRA Port A output Port A input
0010 - DDRB Data Direction, port B
0011 - DDRA Data Direction, port A
0100 - T1L-L T1 Low-Order Latches T1 Low-Order Counter
0101 - T1C-H T1 High-Order Counter
0110 - T1L-L T1 Low-Order Latches
0111 - T1L-H T1 High-Order Latches
1000 - T2C-L T2 Low-Order Latches T2 Low-Order Counter
1001 - T2C-H T2 High-Order Counter
1010 - SR Shift Register
1011 - ACR Auxillary Control Register
1100 - PCR Peripheal Control Register
1101 - IFR Interrupt flag
1110 - IER Interrupt enable
1111 - ORA Same as 0001 but no effect on handshake
4. Read/Write line
If low, write (processor to VIA register)
If high, read (VIA register to processor)
5. Data Bus (DB0-7)
Transfers data between VIA and M68l
6. Reset
Clears all registers to 0.
All I/O set to input, disablse timers, SR, interrupts, etc.
7. IRQ
8. Peripheal A Port (PA0 - PA7)
8 lines. Each can be input or output, depending on value of DDRA
9. Port A Control Lines (CA1/CA2)
Interrupt inputs or handshake outputs
Each line controls an internal interrupt flag w/ enable bit
CA1 controls latching of data on P1 input
10. Peripheal Port B (PB0 - PB7)
Same as Port A, with timers!
PB7 polarity can be controller by timer
PB6 can count pulses w/ second timer
11. Port B control lines (CB1/CB2)
Same as CA
Shift register serial I/O goes through one of these
I'm not going to bother to emulate handshaking
## Notes from "Guide to Macintosh Family Hardware, Second Edition"
- On Mac II+, VIA circuits are custom Apple silicon, like 6522
- VIA1 handles classic Mac functions, VIA2 handles Mac II functions
Mac 128k: VIA x1
Mac 512K: VIA x1
Mac Plus: VIA x1
Mac SE: VIA x1
Mac SE/30: VIA x1
Mac Port: VIA x1
Mac II: VIA x2
Mac IIx: VIA x2
Mac IIcx: VIA x2
Mac IIci: VIA x1, RBV
Mac IIfx: VIA x1, OSS
- RBV = "RAM Based Video Controller"
- RBV is more custom Apple silicon that is a superset of VIA2
- OSS = "Operating System Support"
- Operates synchronously w/ M68k
- Timed by E clock at 783.36 kHz, generated by M68k
- In SE/30, Mac Portable, and Mac II, synchronized w/ glue logic
- 128k/Plus/SE need to wait for E clock to access VIA (1 us)
- SE/30, Portable, II can access VIA w/ no delay (0.5 us)
```c
typedef struct {
uint8_t vBufA; // Data Register A
uint8_t vBufB; // Data Register B
uint8_t vDirA; // Data Direction A (0 = in, 1 = out)
uint8_t vDirB; // Data Direction B (0 = in, 1 = out)
uint8_t vPCR; // Peripheal Control
uint8_t vACR; // Auxiliary Control
uint8_t vTxx; // Event timers (multiple?)
uint8_t vIFR; // Interrupt Flag
uint8_t vIER; // Interrupt Enable
uint8_t vSR; // Shift register
} VIA1_State_t;
```
## Data Register allocations
### Mac 128/512k/Plus ("classic Macintosh")
Bit | Dir | Bit name | Description
------|-----|-----------|-----------------------------------------------------
PA7 | In | vSCCWrReq | SCC Wait/Request, channel A or B
PA6 | Out | vPage2 | 0 = alt. screen buffer, 1 = main screen buffer
PA5 | Out | vHeadSel | Disk SEL line (which floppy head to use)
PA4 | Out | vOverlay | 1 = ROM overlay address map is used (startup only)
PA3 | Out | vSndPg2 | 0 = alt. sound buffer, 1 = main sound buffer
PA2-0 | Out | vSound | Sound Volume (bit 2 is MSB, 111 is max)
PB7 | Out | vSndEnb | 0 = sound enabled
PB6 | In | vH4 | 0 = video beam in display portion of line
PB5 | In | vY2 | Mouse Y2 quadrature signal
PB4 | In | vX2 | Mouse X2 quadrature signal
PB3 | In | vSW | 0 = Mouse button pressed
PB2 | Out | vTCEnb | 0 = RTC enabled
PB1 | Out | vTCCLK | RTC data-clock line
PB0 | I/O | rTCData | RTC serial data line
vSCCWrReq monitors /W/REQA and /W/REQB from SCC, with are OR'd
together. Used to determine when SCC had read a byte during floppy disk
operations, when SCC interrupts are disabled.
### Mac SE
Bit | Dir | Bit name | Description
------|-----|-----------|-----------------------------------------------------
PA7 | In | vSCCWrReq | SCC Wait/Request, channel A or B
PA6 | Out | vPage2 | 0 = alt. screen buffer, 1 = main screen buffer
PA5 | Out | vHeadSel | Disk SEL line (which floppy/head to use)
PA4 | Out | vDriveSel | 0 = upper, 1 = lower
PA3 | Out | vSync | 1 = synchronous modem support, channel A
PA2-0 | Out | vSound | Sound Volume (bit 2 is MSB, 111 is max)
PB7 | Out | vSndEnb | 0 = sound enabled
PB6 | In | vH4 | 0 = video beam in display portion of line
PB5 | In | vFDesk2 | ADB state input 1 (ST1)
PB4 | In | vFDesk1 | ADB state input 0 (ST0)
PB3 | In | vFDBInt | 0 = ADB interrupt
PB2 | Out | vTCEnb | 0 = RTC enabled
PB1 | Out | vTCCLK | RTC data-clock line
PB0 | I/O | rTCData | RTC serial data line
vSync, when low, makes SCC work like classic Mac models where Receive Closk
(RTxC) signal frequency for both channels A and B are 3.672 MHz. When high,
channel A's RTxC signal is provided by the GPi pin from the modem serial port
connector.
Mouse bits are replaced with ADB bits.
### Mac Portable
All bits on data register A are used as an 8-bit bidirectional data bus to
communicate w/ the Power Manager's microprocessor.
Bit | Dir | Bit name | Description
------|-----|-----------|-----------------------------------------------------
PB7 | In | vSCCWrReq | SCC Wait/Request, channel A or B
PB7 | Out | vSndEnb | 0 = sound enabled
PB6 | In | vSndExt | 0 = headphones plugged in (output is stereo)
PB5 | Out | vHeadSel | Floppy disk head select
PB4 | Out | vDriveSel | 0 = upper, 1 = lower
PB3 | Out | vSync | 1 = sync. modem support, channel A (like SE)
PB2 | Out | vTest | Test signal (ignore)
PB1 | In | vPMAck | ACK signal for Power Manager
PB0 | Out | vPMReq | REQ signal for Power Manager
PB7 is vSCCWrReq only if SCC interrupts are disabled.
vSndEnb is a purely software register. OS emulates classic behavior by reading
and making ASC commands if needed.
vTest, if high, forces all power supplies to full power. Only used for testing
by Apple service employees; do not bother implementing.
### Mac II, IIx, IIcx and SE/30 - VIA1
Bit | Dir | Bit name | Description
------|-----|-----------|-----------------------------------------------------
PA7 | In | vSCCWrReq | SCC Wait/Request, channel A or B
PA6 | Out | vPage2 | Screen buffer, like others. Mac SE/30 only.
PA6 | In | CPU.ID1 | Identifies different Mac II models (not SE/30)
PA5 | Out | vHeadSel | Disk SEL line (which floppy/head to use)
PA4 | Out | vOverlay | 1 = ROM overlay address map is used
PA3 | Out | vSync | 1 = synchronous modem support, channel A
PA2-0 | | | Reserved
PB7 | Out | vSndEnb | 0 = sound enable (for compatibility w/ old software)
PB6 | Out | vSyncEnA | VSync ISR enabled (Mac SE/30 only)
PB5 | Out | vFDesk2 | ADB state input 1 (ADB.ST1)
PB4 | Out | vFDesk1 | ADB state input 0 (ADB.ST0)
PB3 | In | vFDBInt | 0 = ADB interrupt (/ADB.INT)
PB2 | Out | vTCEnb | 0 = RTC enabled
PB1 | Out | vTCCLK | RTC data-clock line
PB0 | I/O | rTCData | RTC serial data line
- CPU.ID1 is tied low on Mac II, IIx, and IIcx, and high on IIci/IIfx.
- vOverlay causes TAM to in inaccessible until Overlay signal is deasserted.
Only used during startup.
- PA2-0 are either unused or additional machine ID bits. This is to preserve
software compatibility.
- PB is almost the same as the Macintosh SE.
- vSyncEnA enables or disables interrupt $E (internal video ISR)
This interrupt is not the 60.15Hz VBL request. That's still emulated as-is.
Mac SE/30 system software does not use VBL. It uses $E instead.
### Mac IIci - VIA1
Bit | Dir | Bit name | Description
------|-----|-----------|-----------------------------------------------------
PA7 | In | vSCCWrReq | SCC Wait/Request, channel A or B
PA6 | In | CPU.ID3 | Bit 3 of 4-bit model ID
PA5 | Out | vHeadSel | Disk SEL line (which floppy/head to use)
PA4 | In | CPU.ID2 | Bit 2 of 4-bit model ID
PA3 | Out | vSync | 1 = synchronous modem support, channel A
PA2 | In | CPU.ID1 | Bit 1 of 4-bit model ID
PA1 | In | CPU.ID0 | Bit 0 of 4-bit model ID
PA0 | | | Reserved
PB7 | In | /Par.Err | 0 = parity error
PB6 | In | /Par.En | 0 = parity-checking enabled
PB5 | Out | vFDesk2 | ADB state input 1 (ADB.ST1)
PB4 | Out | vFDesk1 | ADB state input 0 (ADB.ST0)
PB3 | In | vFDBInt | 0 = ADB interrupt (/ADB.INT)
PB2 | Out | vTCEnb | 0 = RTC enabled
PB1 | Out | vTCCLK | RTC data-clock line
PB0 | I/O | rTCData | RTC serial data line
Certain models of the IIci have a "parity generation and checking" chip.
Whenever a byte of RAM is written, the PGC stores a parity bit in bit 9.
Whenever a byte of RAM is read, the PGC reads, compares, and raises
/NMI and /PAR.ERR if /Par.En is enabled.
The parity-checking IIci models are very rare, only available via special
order from Apple. It almost certainly is not worth emulating.
### Mac IIfx - VIA1
Port A is like IIci, except *only* model ID bits are used.
Other functions are controlled by the OSS, IOP, and BIU30.
Port B is the same as the II, except that the ADB lines are unused.
### Model ID table
Model | Value (3..0)
-------------------|--------------
Mac II (early) | 0b0xxx
Mac IIci | 0b1011
Mac IIci w/ parity | 0b1111
Mac IIfx | 0b1101
### Mac II family - VIA2
Renaming ports A and B -> C and D.
Bit | Dir | Bit name | Description
------|-----|------------|-----------------------------------------------------
PC7 | Out | v2RAM1 | RAM-size bit 1 (except IIci)
PC6 | Out | v2RAM0 | RAM-size bit 0 (except IIci)
PC6 | In | v2IRQ0 | ISR from internal video circuits (IIci only)
PC5 | In | v2IRQ6 | ISR from expansion slot $E
PC4 | In | v2IRQ5 | ISR from expansion slot $D
PC3 | In | v2IRQ4 | ISR from expansion slot $C
PC2 | In | v2IRQ3 | ISR from expansion slot $B
PC1 | In | v2IRQ2 | ISR from expansion slot $A
PC0 | In | v2IRQ1 | ISR from expansion slot $9
PD7 | Out | v2VBL | 60.15 Hz timer out
PD7 | Out | /Par.Test | 0 = parity test mode (IIci only)
PD6 | In | v2SNDEXT | 0 = headphones inserted (output is stereo)
PD5 | In | v2TM0A | Transfer mode bit 0 ACK from NuBus
PD4 | In | v2TM1A | Transfer mode bit 1 ACK from NuBus
PD3 | I/O | vFC3 | various; see notes
PD2 | Out | v2PowerOff | 0 = shut off power
PD1 | Out | c2BusLk | 0 = NuBus transactions are locked out
PD0 | Out | /CEnable | 1 = disable cache card (Mac IIci)
PD0 | Out | v2CDis | 0 = disable M68k cache (all other)
v2RAM indicates size of TAM ICs used in bank A.
- 0b00: 256 Kbit
- 0b01: 1 Mbit
- 0b10: 4 Mbit
- 0b11: 16 Mbit
Determines when bank A stops and bank B begins.
Not needed in Mac IIci; M68030's MCU is used instead. Bank B is always at
address $0400_0000.
IIcx only uses slots $9, $A, and $B. IIcx only uses slots $C, $D, and $E.
Mac SE/30 PDS cards can act like a NuBus card in slot $9, $A, or $B. $E can be
generated by internal video logic circuits.
v2SNDEXT is always low in the Mac SE/30, because it always supports stereo
sound. (Speaker is mono, but it has a hardware mixer.) Also, the SE/30 doesn't
have a headphone jack. (much like the iPhone SE 2 ayy lmao)
v2TMxA store error codes from NuBus transations. They can be:
- 0b00: No error
- 0b01: Read/write error, handled by computer
- 0b02: Bus timeout
- 0b03: Bus busy; try again later
Also, used for other NuBus transaction type/size indicators.
v2PowerOff is connected directly to the power supply. Stop emulation when this
goes low.
v2BusLk blocks incoming NuBus messages and sends a "bus busy" response. Meant
to keep DMA from interfering with other things.
Yes, PD0 is the opposite on the Mac IIci compared to every other system. Don't
think about this too hard. (We're probably not emulating cache anyways, so it
doesn't really matter.)
#### vFC3 table
Model | Condition | Dir | Description
------|-----------|-----|-----------------------------------------
II | AMU in | Out | 0 = do 24->32 bit address translation
II | PMMU in | In | 1 = PMMU accessing page table
SE/30 | | In | Always 0
IIx | | | not used
IIcx | | | not used
IIfx | | | not used
IIci | | | 0 = flush cache (in optional cache card)
## Peripheral Control Register
Allows software to set triggering parameters for certain VIA signals.
### All models except Portable - VIA1
| Bit | Dir | Description
|-----|-----|------------------------------------------
| 7 | I/O | Keyboard or ADB data
| 6 | I/O | Keyboard or ADB data
| 5 | I/O | Keyboard or ADB data
| 4 | In | Keyboard or ADB clock (from ext. device)
| 3 | In | One-second interrupt
| 2 | In | One-second interrupt
| 1 | In | One-second interrupt
| 0 | In | VBlank Interrupt (VBL)
- Keyboard data line transfers data from shift register to keyboad, somehow
- One-second interrupt generated by RTC.
- VBL generated by a PAL at 60.15 Hz (once per 16.63 ms)
On Mac Portable, SE/30, or II+, VBL is still 60.15 Hz but not tied to VBlank
### Macintosh Portable
| Bit | Dir | Description
|-----|-----|-------------
| 7 | In | SCSI IRQ
| 6 | In | SCSI IRQ
| 5 | In | SCSI IRQ
| 4 | In | Power manager interrupt
| 3 | In | One-second interrupt
| 2 | In | One-second interrupt
| 1 | In | One-second interrupt
| 0 | In | VBlank Interrupt (VBL)
### Mac SE/30 and Mac II family - VIA2
| Bit | Dir | Description
|-----|-----|-------------
| 7 | I/O | SCSI IRQ
| 6 | I/O | SCSI IRQ
| 5 | I/O | SCSI IRQ
| 4 | In | ASC interrupt
| 3 | In | SCSI DRQ
| 2 | In | SCSI DRQ
| 1 | In | SCSI DRQ
| 0 | In | Slot interrupt
- SCSI IRQ is generated by SCSI to indicate an error condition
- SCSI DRQ indicates that SCSI is ready to transfer data.
- ASC indicates when ASC sound buffer is ready to be reloaded.
- Slot interrupt fires if *any* slot raises an interrupt.
## Auxiliary Control Register
Does things with VIA timers and the shift register.
### All Macs - VIA1 and VIA2
| Bit | Dir | Description
|-----|-----|-------------
| 7 | In | Timer T1 - vSndEnb (0 = no change, 1 = invert)
| 6 | In | Timer T1 - Mode (0 = one-shot, 1 = pulse)
| 5 | In | Timer T2 - Mode (0 = one-shot, 1 = tick on PB6 falling edge)
| 4 | In | Keyboard or ADB bit-shift operation (VIA1 only; not Portable)
| 3 | In | Keyboard or ADB bit-shift operation (VIA1 only; not Portable)
| 2 | In | Keyboard or ADB bit-shift operation (VIA1 only; not Portable)
| 1 | In | Input data latch for DR B (1 = enable, 0 = disable)
| 0 | In | Input data latch for DR A (1 = enable, 0 = disable)
- T1 can be one-shot interval timer or a pulse generator
- In one-shot mode, decrement counter every 1.2766 us, fire ISR when 0.
- In pulse mode, T1 can be loaded with a value that T1 will reload
after hitting zero. Can also invert DB7 (sound enable) on each loop.
- On VIA2, this generates the 60.15 Hz interrupt signal
- Some games for 128k/512k/Plus used timer T1 to toggle sound on and off to
generate a square wave. This is broken on models with the ASC.
- T2 can be used to get the current Y position of the electron beam for the
Mac 128k, 512k, Plus, and SE. (That's PB6)
- On the Mac II, this interrupt on VIA 2 counts the number of times you've
inserted your headphones. How... useful... (ditto w/ Portable on VIA1)
- Portable uses the Power Manager as an ADB controller.
- Bits 0 and 1, if enabled, latch any high signal until cleared. If disabled,
the values are updated real-time.
- Bits 0-5 are *only* modified by System software.
- Bits 4-2 should be 0b011, or 0b000 on startup.
### Macintosh IIci
There is no ACR; this is handled by the RBV.
## Shift Register
Used for keyboard or ADB I/O. Not used on VIA2 or the Portable.
## Event timers
This is where the current state of T1 and T2 are stored.
See the SY6522 pin summary for details.
Uses the E clock generated by the M68k as reference, so these counters
decrement every 1.2766 us.
Values must be stored per-byte; there is no per-word access. Writing to high
byte *starts* the timer.
- T1 on VIA1 is used by system sound driver.
- T2 on VIA1 is use by disk driver to time disk I/O events.
- T1 on VIA2 generates VBL every 60.15 Hz
- T2 on VIA2 is (as of 1991) unused.
## Interrupt Flag Register
### VIA1 - All except Portable
- IRQ7 - IRQ (all enabled VIA interrupts)
- IRQ6 - Timer 1 = 0
- IRQ5 - Timer 2 = 0
- IRQ4 - Keyboard/ADB clock received
- IRQ3 - Keyboard/ADB data bit received
- IRQ2 - Keyboard/ADB data ready (finished shifting 8 bits in or out)
- IRQ1 - VBL
- IRQ0 - One-second interrupt from RTC
### VIA1 - Portable
- IRQ7 - IRQ (all enabled VIA interrupts)
- IRQ6 - Timer 1 = 0
- IRQ5 - Timer 2 = 0
- IRQ4 - SCSI IRQ received
- IRQ3 - Power Manager ISR received
- IRQ2 - Not used
- IRQ1 - VBL
- IRQ0 - One-second interrupt from RTC
### VIA2
- IRQ7 - IRQ (all enabled VIA interrupts)
- IRQ6 - Timer 1 = 0
- IRQ5 - Timer 2 = 0
- IRQ4 - ASC interrupt
- IRQ3 - SCSI IRQ
- IRQ2 - /EXP.IRQ (Mac IIci only)
- IRQ1 - Slot interrupt
- IRQ0 - SCSI DRQ
Mac IIci uses RBV, Mac IIfx uses OSS to control interrupt functions.
## Interrupt Enable Register
Yep, that's a thing! One bit per interrupt.
When writing, bit 7 signals set (1) or clear (0), and the other bits apply
that action to the corresponding IRQ. When reading, bit 7 is always 1.
When an interrupt is tabled, IF is still updated, but no interrupt is generated.
And *that's* the VIA. The whole dang thing. That wasn't too hard, huh?

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +0,0 @@
/*
HW/VIA/VIA2EMDV.h
Copyright (C) 2004 Philip Cummins, Paul C. Pratt
You can redistribute this file and/or modify it under the terms
of version 2 of the GNU General Public License as published by
the Free Software Foundation. You should have received a copy
of the license along with this file; see the file COPYING.
This file 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
license for more details.
*/
#ifndef VIA2EMDV_H
#define VIA2EMDV_H
EXPORTPROC VIA2_Zap(void);
EXPORTPROC VIA2_Reset(void);
EXPORTFUNC uint32_t VIA2_Access(uint32_t Data, bool WriteMem, CPTR addr);
EXPORTPROC VIA2_ExtraTimeBegin(void);
EXPORTPROC VIA2_ExtraTimeEnd(void);
#ifdef VIA2_iCA1_PulseNtfy
EXPORTPROC VIA2_iCA1_PulseNtfy(void);
#endif
#ifdef VIA2_iCA2_PulseNtfy
EXPORTPROC VIA2_iCA2_PulseNtfy(void);
#endif
#ifdef VIA2_iCB1_PulseNtfy
EXPORTPROC VIA2_iCB1_PulseNtfy(void);
#endif
#ifdef VIA2_iCB2_PulseNtfy
EXPORTPROC VIA2_iCB2_PulseNtfy(void);
#endif
EXPORTPROC VIA2_DoTimer1Check(void);
EXPORTPROC VIA2_DoTimer2Check(void);
EXPORTFUNC uint16_t VIA2_GetT1InvertTime(void);
EXPORTPROC VIA2_ShiftInData(uint8_t v);
EXPORTFUNC uint8_t VIA2_ShiftOutData(void);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,40 +1,85 @@
/*
HW/VIA/VIAEMDEV.h
Versatile Interface Adapter EMulated DEVice
Copyright (C) 2004 Philip Cummins, Paul C. Pratt
You can redistribute this file and/or modify it under the terms
of version 2 of the GNU General Public License as published by
the Free Software Foundation. You should have received a copy
of the license along with this file; see the file COPYING.
This file 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
license for more details.
Emulates the Synertek SY6522 VIA in every system that uvMac targets.
This code rewritten for target-independence and non-enum based config
*/
#ifndef VIAEMDEV_H
#define VIAEMDEV_H
#pragma once
#include <stdint.h>
#include <stdbool.h>
bool VIA1_Zap(void);
void VIA1_Reset(void);
/// TYPEDEFS /////////////////////////////////////////////////////////////////
uint32_t VIA1_Access(uint32_t Data, bool WriteMem, uint32_t addr);
/* Mac II has two VIAs, all others have one */
#define VIA_MAXNUM (2)
void VIA1_ExtraTimeBegin(void);
void VIA1_ExtraTimeEnd(void);
void VIA1_iCA1_PulseNtfy(void);
void VIA1_iCA2_PulseNtfy(void);
void VIA1_iCB1_PulseNtfy(void);
void VIA1_iCB2_PulseNtfy(void);
void VIA1_DoTimer1Check(void);
void VIA1_DoTimer2Check(void);
/* VIA interrupt handler signature */
typedef void (*VIA_ISR_t)(void);
uint16_t VIA1_GetT1InvertTime(void);
/* Names from Guide to Macintosh Family Hardware, Second Edition, pg. 159 */
typedef struct {
uint8_t vBufA; // Data Register A
uint8_t vBufB; // Data Register B
uint8_t vDirA; // Data Direction A (0 = in, 1 = out)
uint8_t vDirB; // Data Direction B (0 = in, 1 = out)
uint8_t vPCR; // Peripheal Control
uint8_t vACR; // Auxiliary Control
uint8_t vIFR; // Interrupt Flag
uint8_t vIER; // Interrupt Enable
uint8_t vSR; // Shift register
uint16_t vT1L; // Timer 1 latch
uint16_t vT1C; // Timer 1 counter
uint16_t vT2L; // Timer 2 latch
uint16_t vT2C; // Timer 2 counter
VIA_ISR_t vISR[8]; // ISRs to automatically call when interrupt is raised
} VIA_State_t;
void VIA1_ShiftInData(uint8_t v);
uint8_t VIA1_ShiftOutData(void);
/* Names from SY6522 datasheet */
typedef enum {
rIRB = 0, // Data Register B
rIRA = 1, // Data Register A
rDDRB = 2, // Data Direction B
rDDRA = 3, // Data Direction A
rT1CL = 4, // Timer 1 Counter (low)
rT1CH = 5, // Timer 1 Counter (high)
rT1LL = 6, // Timer 1 Latches (low)
rT1LH = 7, // Timer 1 Latches (high)
rT2CL = 8, // Timer 1 Counter (low)
rT2CH = 9, // Timer 1 Latches (high)
rSR = 10, // Shift Register
rACR = 11, // Auxiliary Control
rPCR = 12, // Peripheal Control
rIFR = 13, // Interrupt Flag
rIER = 14, // Interrupt Enable
rORA = 15, // duplicate of IRA w/o handshake (unused)
rINVALID = 16, // end of list
} VIA_Register_t;
#endif
/// PUBLIC FUNCTIONS /////////////////////////////////////////////////////////
// Hardware reset
bool VIA_Zap(void);
// Software reset
void VIA_Reset(void);
// Raise an interrupt by irq number, calling registered ISR if required
void VIA_RaiseInterrupt(uint8_t id, uint8_t irq);
// Register a VIA interrupt service routine
void VIA_RegisterISR(uint8_t id, uint8_t irq, VIA_ISR_t isr);
// Tick all timers by one step (call every 1.2766 us)
void VIA_TickTimers();
// Write to a register
void VIA_Write(uint8_t id, VIA_Register_t reg, uint8_t data);
// Read to a register
uint8_t VIA_Read(uint8_t id, VIA_Register_t reg);
// Read a single bit
bool VIA_ReadBit(uint8_t id, VIA_Register_t reg, uint8_t bit);
// Write a single bit
void VIA_WriteBit(uint8_t id, VIA_Register_t reg, uint8_t bit, bool set);
// NOTE: for these, raise the interrupt manually w/ VIA_RaiseInterrupt
void VIA_ShiftInData(uint8_t id, uint8_t v);
uint8_t VIA_ShiftOutData(uint8_t id);

View File

@ -28,7 +28,6 @@
#include "HW/M68K/M68KITAB.h"
#include "HW/M68K/MINEM68K.h"
#include "HW/VIA/VIAEMDEV.h"
#include "HW/VIA/VIA2EMDV.h"
#include "HW/DISK/IWMEMDEV.h"
#include "HW/SCC/SCCEMDEV.h"
#include "HW/RTC/RTCEMDEV.h"
@ -133,23 +132,13 @@ const DevMethods_t DEVICES[] = {
},
// VIA1
{
.init = VIA1_Zap,
.reset = VIA1_Reset,
.starttick = NULL,
.init = VIA_Zap,
.reset = VIA_Reset,
.starttick = VIA_TickTimers,
.endtick = NULL,
.subtick = NULL,
.timebegin = VIA1_ExtraTimeBegin,
.timeend = VIA1_ExtraTimeEnd,
},
// VIA2
{
.init = NULL,
.reset = EmVIA2 ? VIA2_Zap : NULL,
.starttick = NULL,
.endtick = NULL,
.subtick = NULL,
.timebegin = EmVIA2 ? VIA2_ExtraTimeBegin : NULL,
.timeend = EmVIA2 ? VIA2_ExtraTimeEnd : NULL,
.timebegin = NULL,
.timeend = NULL,
},
// Sony disk drive
{