mirror of
https://github.com/InvisibleUp/uvmac.git
synced 2024-11-21 15:34:08 +00:00
Begin a WIP rewrite of VIAEMDEV.c
Beyond the over-reliance on #ifdefs for servicing interrupts (which breaks pretty much everything), my gut's telling me that it's just not the best implementation. IDK. Let's see where this goes.
This commit is contained in:
parent
fc3b393329
commit
514add67d8
300
_work/VIAEMDEV.c
Normal file
300
_work/VIAEMDEV.c
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
/*
|
||||
Versatile Interface Adapter EMulated DEVice
|
||||
|
||||
Emulates the VIA found in the Mac Plus.
|
||||
Emulates the Synertek SY6522 VIA found up until the Mac Plus.
|
||||
|
||||
This code adapted from vMac by Philip Cummins.
|
||||
*/
|
||||
|
@ -16,32 +16,25 @@
|
||||
|
||||
#ifndef VIAEMDEV_H
|
||||
#define VIAEMDEV_H
|
||||
#include <stdint.h>
|
||||
|
||||
EXPORTPROC VIA1_Zap(void);
|
||||
EXPORTPROC VIA1_Reset(void);
|
||||
void VIA1_Zap(void);
|
||||
void VIA1_Reset(void);
|
||||
|
||||
EXPORTFUNC uint32_t VIA1_Access(uint32_t Data, bool WriteMem, CPTR addr);
|
||||
uint32_t VIA1_Access(uint32_t Data, bool WriteMem, uint32_t addr);
|
||||
|
||||
EXPORTPROC VIA1_ExtraTimeBegin(void);
|
||||
EXPORTPROC VIA1_ExtraTimeEnd(void);
|
||||
#ifdef VIA1_iCA1_PulseNtfy
|
||||
EXPORTPROC VIA1_iCA1_PulseNtfy(void);
|
||||
#endif
|
||||
#ifdef VIA1_iCA2_PulseNtfy
|
||||
EXPORTPROC VIA1_iCA2_PulseNtfy(void);
|
||||
#endif
|
||||
#ifdef VIA1_iCB1_PulseNtfy
|
||||
EXPORTPROC VIA1_iCB1_PulseNtfy(void);
|
||||
#endif
|
||||
#ifdef VIA1_iCB2_PulseNtfy
|
||||
EXPORTPROC VIA1_iCB2_PulseNtfy(void);
|
||||
#endif
|
||||
EXPORTPROC VIA1_DoTimer1Check(void);
|
||||
EXPORTPROC VIA1_DoTimer2Check(void);
|
||||
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);
|
||||
|
||||
EXPORTFUNC uint16_t VIA1_GetT1InvertTime(void);
|
||||
uint16_t VIA1_GetT1InvertTime(void);
|
||||
|
||||
EXPORTPROC VIA1_ShiftInData(uint8_t v);
|
||||
EXPORTFUNC uint8_t VIA1_ShiftOutData(void);
|
||||
void VIA1_ShiftInData(uint8_t v);
|
||||
uint8_t VIA1_ShiftOutData(void);
|
||||
|
||||
#endif
|
||||
|
@ -137,8 +137,8 @@ const DevMethods_t DEVICES[] = {
|
||||
},
|
||||
// VIA1
|
||||
{
|
||||
.init = NULL,
|
||||
.reset = VIA1_Zap,
|
||||
.init = VIA1_Zap,
|
||||
.reset = VIA1_Reset,
|
||||
.starttick = NULL,
|
||||
.endtick = NULL,
|
||||
.subtick = NULL,
|
||||
|
Loading…
Reference in New Issue
Block a user