ac6502 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CPU.d.ts +162 -0
- package/dist/components/Cart.d.ts +9 -0
- package/dist/components/IO/DevOutputBoard.d.ts +19 -0
- package/dist/components/IO/DevOutputBoard.js +33 -0
- package/dist/components/IO/DevOutputBoard.js.map +1 -0
- package/dist/components/IO/EmptyCard.d.ts +9 -0
- package/dist/components/IO/GPIOAttachments/GPIOAttachment.d.ts +112 -0
- package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.d.ts +53 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.d.ts +63 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.d.ts +44 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeypadAttachment.d.ts +47 -0
- package/dist/components/IO/GPIOAttachments/GPIOLCDAttachment.d.ts +110 -0
- package/dist/components/IO/GPIOCard.d.ts +105 -0
- package/dist/components/IO/RAMCard.d.ts +37 -0
- package/dist/components/IO/RTCCard.d.ts +107 -0
- package/dist/components/IO/SerialCard.d.ts +76 -0
- package/dist/components/IO/SoundCard.d.ts +120 -0
- package/dist/components/IO/StorageCard.d.ts +74 -0
- package/dist/components/IO/VideoCard.d.ts +141 -0
- package/dist/components/IO.d.ts +8 -0
- package/dist/components/Machine.d.ts +66 -0
- package/dist/components/Machine.js +34 -10
- package/dist/components/Machine.js.map +1 -1
- package/dist/components/RAM.d.ts +9 -0
- package/dist/components/ROM.d.ts +9 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +34 -15
- package/dist/index.js.map +1 -1
- package/dist/lib.d.ts +22 -0
- package/dist/lib.js +47 -0
- package/dist/lib.js.map +1 -0
- package/dist/tests/CPU.test.d.ts +1 -0
- package/dist/tests/Cart.test.d.ts +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOAttachment.test.d.ts +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.d.ts +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.d.ts +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.d.ts +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeypadAttachment.test.d.ts +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOLCDAttachment.test.d.ts +1 -0
- package/dist/tests/IO/GPIOCard.test.d.ts +1 -0
- package/dist/tests/IO/RAMCard.test.d.ts +1 -0
- package/dist/tests/IO/RTCCard.test.d.ts +1 -0
- package/dist/tests/IO/SerialCard.test.d.ts +1 -0
- package/dist/tests/IO/SoundCard.test.d.ts +1 -0
- package/dist/tests/IO/StorageCard.test.d.ts +1 -0
- package/dist/tests/IO/VideoCard.test.d.ts +1 -0
- package/dist/tests/Machine.test.d.ts +1 -0
- package/dist/tests/RAM.test.d.ts +1 -0
- package/dist/tests/ROM.test.d.ts +1 -0
- package/package.json +5 -3
- package/src/components/IO/DevOutputBoard.ts +34 -0
- package/src/components/Machine.ts +36 -10
- package/src/index.ts +34 -17
- package/src/lib.ts +27 -0
- package/tsconfig.json +1 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { IO } from '../IO';
|
|
2
|
+
import { GPIOAttachment } from './GPIOAttachments/GPIOAttachment';
|
|
3
|
+
/**
|
|
4
|
+
* GPIOCard - Emulates the 65C22 VIA (Versatile Interface Adapter)
|
|
5
|
+
*
|
|
6
|
+
* The 65C22 VIA provides:
|
|
7
|
+
* - Two 8-bit bidirectional I/O ports (Port A and Port B)
|
|
8
|
+
* - Two 16-bit timers with interrupt generation
|
|
9
|
+
* - Shift register for serial I/O
|
|
10
|
+
* - Handshaking lines for data transfer
|
|
11
|
+
*/
|
|
12
|
+
export declare class GPIOCard implements IO {
|
|
13
|
+
private static readonly VIA_ORB;
|
|
14
|
+
private static readonly VIA_ORA;
|
|
15
|
+
private static readonly VIA_DDRB;
|
|
16
|
+
private static readonly VIA_DDRA;
|
|
17
|
+
private static readonly VIA_T1CL;
|
|
18
|
+
private static readonly VIA_T1CH;
|
|
19
|
+
private static readonly VIA_T1LL;
|
|
20
|
+
private static readonly VIA_T1LH;
|
|
21
|
+
private static readonly VIA_T2CL;
|
|
22
|
+
private static readonly VIA_T2CH;
|
|
23
|
+
private static readonly VIA_SR;
|
|
24
|
+
private static readonly VIA_ACR;
|
|
25
|
+
private static readonly VIA_PCR;
|
|
26
|
+
private static readonly VIA_IFR;
|
|
27
|
+
private static readonly VIA_IER;
|
|
28
|
+
private static readonly VIA_ORA_NH;
|
|
29
|
+
private static readonly IRQ_CA2;
|
|
30
|
+
private static readonly IRQ_CA1;
|
|
31
|
+
private static readonly IRQ_SR;
|
|
32
|
+
private static readonly IRQ_CB2;
|
|
33
|
+
private static readonly IRQ_CB1;
|
|
34
|
+
private static readonly IRQ_T2;
|
|
35
|
+
private static readonly IRQ_T1;
|
|
36
|
+
private static readonly IRQ_IRQ;
|
|
37
|
+
private static readonly MAX_ATTACHMENTS_PER_PORT;
|
|
38
|
+
private regORB;
|
|
39
|
+
private regORA;
|
|
40
|
+
private regDDRB;
|
|
41
|
+
private regDDRA;
|
|
42
|
+
private regT1C;
|
|
43
|
+
private regT1L;
|
|
44
|
+
private regT2C;
|
|
45
|
+
private regT2L;
|
|
46
|
+
private regSR;
|
|
47
|
+
private regACR;
|
|
48
|
+
private regPCR;
|
|
49
|
+
private regIFR;
|
|
50
|
+
private regIER;
|
|
51
|
+
private CA1;
|
|
52
|
+
private CA2;
|
|
53
|
+
private CB1;
|
|
54
|
+
private CB2;
|
|
55
|
+
private T1_running;
|
|
56
|
+
private T2_running;
|
|
57
|
+
private T1_IRQ_enabled;
|
|
58
|
+
private T2_IRQ_enabled;
|
|
59
|
+
private tickCounter;
|
|
60
|
+
private ticksPerMicrosecond;
|
|
61
|
+
private portA_attachments;
|
|
62
|
+
private portB_attachments;
|
|
63
|
+
private portA_attachmentCount;
|
|
64
|
+
private portB_attachmentCount;
|
|
65
|
+
raiseIRQ: () => void;
|
|
66
|
+
raiseNMI: () => void;
|
|
67
|
+
constructor();
|
|
68
|
+
reset(coldStart: boolean): void;
|
|
69
|
+
read(address: number): number;
|
|
70
|
+
write(address: number, data: number): void;
|
|
71
|
+
tick(frequency: number): void;
|
|
72
|
+
private updateIRQ;
|
|
73
|
+
private setIRQFlag;
|
|
74
|
+
private clearIRQFlag;
|
|
75
|
+
private readPortA;
|
|
76
|
+
private readPortB;
|
|
77
|
+
private writePortA;
|
|
78
|
+
private writePortB;
|
|
79
|
+
private updateCA2;
|
|
80
|
+
private updateCB2;
|
|
81
|
+
private notifyAttachmentsControlLines;
|
|
82
|
+
private sortAttachmentsByPriority;
|
|
83
|
+
/**
|
|
84
|
+
* Attach a GPIO device to Port A
|
|
85
|
+
* @param attachment - The attachment to add
|
|
86
|
+
*/
|
|
87
|
+
attachToPortA(attachment: GPIOAttachment): void;
|
|
88
|
+
/**
|
|
89
|
+
* Attach a GPIO device to Port B
|
|
90
|
+
* @param attachment - The attachment to add
|
|
91
|
+
*/
|
|
92
|
+
attachToPortB(attachment: GPIOAttachment): void;
|
|
93
|
+
/**
|
|
94
|
+
* Get a Port A attachment by index
|
|
95
|
+
* @param index - The attachment index
|
|
96
|
+
* @returns The attachment or null if not found
|
|
97
|
+
*/
|
|
98
|
+
getPortAAttachment(index: number): GPIOAttachment | null;
|
|
99
|
+
/**
|
|
100
|
+
* Get a Port B attachment by index
|
|
101
|
+
* @param index - The attachment index
|
|
102
|
+
* @returns The attachment or null if not found
|
|
103
|
+
*/
|
|
104
|
+
getPortBAttachment(index: number): GPIOAttachment | null;
|
|
105
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { IO } from '../IO';
|
|
2
|
+
/**
|
|
3
|
+
* RAMCard - Emulates banked RAM with 256KB total capacity
|
|
4
|
+
*
|
|
5
|
+
* Provides 256KB of banked RAM divided into 256 banks of 1KB each.
|
|
6
|
+
* A bank control register at address 0x3FF selects which bank is currently visible.
|
|
7
|
+
*
|
|
8
|
+
* Address Map:
|
|
9
|
+
* $000-$3FE: Bank data (1K window into selected bank)
|
|
10
|
+
* $3FF: Bank control register (read/write)
|
|
11
|
+
*/
|
|
12
|
+
export declare class RAMCard implements IO {
|
|
13
|
+
static TOTAL_SIZE: number;
|
|
14
|
+
static BANK_SIZE: number;
|
|
15
|
+
static NUM_BANKS: number;
|
|
16
|
+
static BANK_CONTROL_REGISTER: number;
|
|
17
|
+
data: number[];
|
|
18
|
+
currentBank: number;
|
|
19
|
+
raiseIRQ: () => void;
|
|
20
|
+
raiseNMI: () => void;
|
|
21
|
+
/**
|
|
22
|
+
* Read from RAM or bank control register
|
|
23
|
+
*/
|
|
24
|
+
read(address: number): number;
|
|
25
|
+
/**
|
|
26
|
+
* Write to RAM or bank control register
|
|
27
|
+
*/
|
|
28
|
+
write(address: number, data: number): void;
|
|
29
|
+
/**
|
|
30
|
+
* Tick - no timing behavior for RAM
|
|
31
|
+
*/
|
|
32
|
+
tick(frequency: number): void;
|
|
33
|
+
/**
|
|
34
|
+
* Reset the RAM card
|
|
35
|
+
*/
|
|
36
|
+
reset(coldStart: boolean): void;
|
|
37
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { IO } from '../IO';
|
|
2
|
+
/**
|
|
3
|
+
* DS1511Y Real-Time Clock IC Emulation
|
|
4
|
+
*
|
|
5
|
+
* Register Map (0x00-0x1F):
|
|
6
|
+
* 0x00: Seconds (BCD, 00-59)
|
|
7
|
+
* 0x01: Minutes (BCD, 00-59)
|
|
8
|
+
* 0x02: Hours (BCD, 00-23)
|
|
9
|
+
* 0x03: Day of Week (1-7, 1=Sunday)
|
|
10
|
+
* 0x04: Date (BCD, 01-31)
|
|
11
|
+
* 0x05: Month (BCD, 01-12) + Control bits (EOSC, E32K)
|
|
12
|
+
* 0x06: Year (BCD, 00-99)
|
|
13
|
+
* 0x07: Century (BCD, 00-39)
|
|
14
|
+
* 0x08: Alarm Seconds (BCD, 00-59) + AM1 bit
|
|
15
|
+
* 0x09: Alarm Minutes (BCD, 00-59) + AM2 bit
|
|
16
|
+
* 0x0A: Alarm Hours (BCD, 00-23) + AM3 bit
|
|
17
|
+
* 0x0B: Alarm Day/Date + AM4, DY/DT bits
|
|
18
|
+
* 0x0C: Watchdog (0.1 Second and 0.01 Second)
|
|
19
|
+
* 0x0D: Watchdog (0.1 Second and Second)
|
|
20
|
+
* 0x0E: Control A (BLF1, BLF2, PBS, PAB, TDF, KSF, WDF, IRQF)
|
|
21
|
+
* 0x0F: Control B (TE, CS, BME, TPE, TIE, KIE, WDE, WDS)
|
|
22
|
+
* 0x10: RAM Address (Extended RAM Address pointer)
|
|
23
|
+
* 0x11: Reserved
|
|
24
|
+
* 0x12: Reserved
|
|
25
|
+
* 0x13: RAM Data (Extended RAM Data at address pointed to by 0x10)
|
|
26
|
+
*/
|
|
27
|
+
export declare class RTCCard implements IO {
|
|
28
|
+
raiseIRQ: () => void;
|
|
29
|
+
raiseNMI: () => void;
|
|
30
|
+
private userSeconds;
|
|
31
|
+
private userMinutes;
|
|
32
|
+
private userHours;
|
|
33
|
+
private userDayOfWeek;
|
|
34
|
+
private userDate;
|
|
35
|
+
private userMonth;
|
|
36
|
+
private monthControl;
|
|
37
|
+
private userYear;
|
|
38
|
+
private userCentury;
|
|
39
|
+
private internalSeconds;
|
|
40
|
+
private internalMinutes;
|
|
41
|
+
private internalHours;
|
|
42
|
+
private internalDayOfWeek;
|
|
43
|
+
private internalDate;
|
|
44
|
+
private internalMonth;
|
|
45
|
+
private internalYear;
|
|
46
|
+
private internalCentury;
|
|
47
|
+
private alarmSeconds;
|
|
48
|
+
private alarmMinutes;
|
|
49
|
+
private alarmHours;
|
|
50
|
+
private alarmDayDate;
|
|
51
|
+
private watchdog1;
|
|
52
|
+
private watchdog2;
|
|
53
|
+
private watchdogCounterCentis;
|
|
54
|
+
private watchdogCycleCounter;
|
|
55
|
+
private controlA;
|
|
56
|
+
private controlB;
|
|
57
|
+
private ramAddress;
|
|
58
|
+
private ramData;
|
|
59
|
+
private cycleCounter;
|
|
60
|
+
private cpuFrequency;
|
|
61
|
+
private transferCycleCounter;
|
|
62
|
+
private pendingUserToInternal;
|
|
63
|
+
private userSyncNeeded;
|
|
64
|
+
private lastTEEnabled;
|
|
65
|
+
constructor();
|
|
66
|
+
/**
|
|
67
|
+
* Initialize RTC with current system time
|
|
68
|
+
*/
|
|
69
|
+
private initializeWithCurrentTime;
|
|
70
|
+
/**
|
|
71
|
+
* Convert decimal to BCD (Binary Coded Decimal)
|
|
72
|
+
*/
|
|
73
|
+
private decimalToBCD;
|
|
74
|
+
/**
|
|
75
|
+
* Convert BCD to decimal
|
|
76
|
+
*/
|
|
77
|
+
private bcdToDecimal;
|
|
78
|
+
/**
|
|
79
|
+
* Get the number of days in a month
|
|
80
|
+
*/
|
|
81
|
+
private getDaysInMonth;
|
|
82
|
+
/**
|
|
83
|
+
* Get next day of week
|
|
84
|
+
*/
|
|
85
|
+
private getNextDayOfWeek;
|
|
86
|
+
private copyInternalToUser;
|
|
87
|
+
private copyUserToInternal;
|
|
88
|
+
private getTransferCyclesRequired;
|
|
89
|
+
private markUserTimeWrite;
|
|
90
|
+
/**
|
|
91
|
+
* Increment time by one second
|
|
92
|
+
*/
|
|
93
|
+
private incrementTime;
|
|
94
|
+
/**
|
|
95
|
+
* Check if alarm should trigger
|
|
96
|
+
*/
|
|
97
|
+
private checkAlarm;
|
|
98
|
+
private raiseInterruptIfEnabled;
|
|
99
|
+
private setKickstartFlag;
|
|
100
|
+
private decodeWatchdogCentis;
|
|
101
|
+
private reloadWatchdog;
|
|
102
|
+
private stepWatchdog;
|
|
103
|
+
read(address: number): number;
|
|
104
|
+
write(address: number, data: number): void;
|
|
105
|
+
tick(frequency: number): void;
|
|
106
|
+
reset(coldStart: boolean): void;
|
|
107
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { IO } from '../IO';
|
|
2
|
+
/**
|
|
3
|
+
* SerialCard - Emulates a 6551 ACIA (Asynchronous Communications Interface Adapter)
|
|
4
|
+
*
|
|
5
|
+
* Register Map:
|
|
6
|
+
* $00: Data Register (read/write)
|
|
7
|
+
* $01: Status Register (read) / Programmed Reset (write)
|
|
8
|
+
* $02: Command Register (write)
|
|
9
|
+
* $03: Control Register (write)
|
|
10
|
+
*/
|
|
11
|
+
export declare class SerialCard implements IO {
|
|
12
|
+
raiseIRQ: () => void;
|
|
13
|
+
raiseNMI: () => void;
|
|
14
|
+
transmit?: (data: number) => void;
|
|
15
|
+
private dataRegister;
|
|
16
|
+
private statusRegister;
|
|
17
|
+
private commandRegister;
|
|
18
|
+
private controlRegister;
|
|
19
|
+
private transmitBuffer;
|
|
20
|
+
private receiveBuffer;
|
|
21
|
+
private parityError;
|
|
22
|
+
private framingError;
|
|
23
|
+
private overrun;
|
|
24
|
+
private irqFlag;
|
|
25
|
+
private echoMode;
|
|
26
|
+
private cycleCounter;
|
|
27
|
+
private baudRate;
|
|
28
|
+
/**
|
|
29
|
+
* Read from ACIA register
|
|
30
|
+
*/
|
|
31
|
+
read(address: number): number;
|
|
32
|
+
/**
|
|
33
|
+
* Write to ACIA register
|
|
34
|
+
*/
|
|
35
|
+
write(address: number, data: number): void;
|
|
36
|
+
/**
|
|
37
|
+
* Read data from receive buffer
|
|
38
|
+
*/
|
|
39
|
+
private readData;
|
|
40
|
+
/**
|
|
41
|
+
* Write data to transmit buffer
|
|
42
|
+
*/
|
|
43
|
+
private writeData;
|
|
44
|
+
/**
|
|
45
|
+
* Read status register
|
|
46
|
+
*/
|
|
47
|
+
private readStatus;
|
|
48
|
+
/**
|
|
49
|
+
* Write to command register
|
|
50
|
+
*/
|
|
51
|
+
private writeCommand;
|
|
52
|
+
/**
|
|
53
|
+
* Write to control register
|
|
54
|
+
*/
|
|
55
|
+
private writeControl;
|
|
56
|
+
/**
|
|
57
|
+
* Get baud rate from control register code
|
|
58
|
+
*/
|
|
59
|
+
private getBaudRate;
|
|
60
|
+
/**
|
|
61
|
+
* Programmed reset
|
|
62
|
+
*/
|
|
63
|
+
private programmedReset;
|
|
64
|
+
/**
|
|
65
|
+
* Tick - emulate ACIA timing
|
|
66
|
+
*/
|
|
67
|
+
tick(frequency: number): void;
|
|
68
|
+
/**
|
|
69
|
+
* Reset the ACIA
|
|
70
|
+
*/
|
|
71
|
+
reset(coldStart: boolean): void;
|
|
72
|
+
/**
|
|
73
|
+
* Receive data from external source
|
|
74
|
+
*/
|
|
75
|
+
onData(data: number): void;
|
|
76
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { IO } from '../IO';
|
|
2
|
+
/**
|
|
3
|
+
* MOS 6581 SID (Sound Interface Device) Emulation
|
|
4
|
+
*
|
|
5
|
+
* Register Map ($00-$1C):
|
|
6
|
+
* Voice 1: $00-$06
|
|
7
|
+
* Voice 2: $07-$0D
|
|
8
|
+
* Voice 3: $0E-$14
|
|
9
|
+
* Filter: $15-$17
|
|
10
|
+
* Volume: $18
|
|
11
|
+
* Paddle: $19-$1A (read-only)
|
|
12
|
+
* OSC 3: $1B (read-only)
|
|
13
|
+
* ENV 3: $1C (read-only)
|
|
14
|
+
*
|
|
15
|
+
* Each voice has:
|
|
16
|
+
* Frequency (16-bit, lo/hi)
|
|
17
|
+
* Pulse Width (12-bit, lo/hi)
|
|
18
|
+
* Control Register (waveform select, gate, sync, ring mod, test)
|
|
19
|
+
* Attack/Decay (4-bit each)
|
|
20
|
+
* Sustain/Release (4-bit each)
|
|
21
|
+
*
|
|
22
|
+
* Waveforms: Triangle, Sawtooth, Pulse, Noise
|
|
23
|
+
* Filter: 12-bit cutoff, resonance, low/band/high-pass, voice routing
|
|
24
|
+
*
|
|
25
|
+
* Clock rate: ~1 MHz (NTSC: 1,022,727 Hz, PAL: 985,248 Hz)
|
|
26
|
+
* Output: mono audio samples passed via callback to the host emulator
|
|
27
|
+
*
|
|
28
|
+
* Reference: MOS 6581 SID datasheet, reSID by Dag Lem
|
|
29
|
+
*/
|
|
30
|
+
/** Default SID clock rate (NTSC) */
|
|
31
|
+
export declare const SID_CLOCK_NTSC = 1022727;
|
|
32
|
+
export declare const SID_CLOCK_PAL = 985248;
|
|
33
|
+
export declare const enum EnvelopeState {
|
|
34
|
+
ATTACK = 0,
|
|
35
|
+
DECAY = 1,
|
|
36
|
+
SUSTAIN = 2,
|
|
37
|
+
RELEASE = 3
|
|
38
|
+
}
|
|
39
|
+
export declare class SIDVoice {
|
|
40
|
+
accumulator: number;
|
|
41
|
+
frequency: number;
|
|
42
|
+
pulseWidth: number;
|
|
43
|
+
control: number;
|
|
44
|
+
prevGate: boolean;
|
|
45
|
+
noiseShift: number;
|
|
46
|
+
waveformOutput: number;
|
|
47
|
+
envelopeState: EnvelopeState;
|
|
48
|
+
envelopeLevel: number;
|
|
49
|
+
envelopeCounter: number;
|
|
50
|
+
attackRate: number;
|
|
51
|
+
decayRate: number;
|
|
52
|
+
sustainLevel: number;
|
|
53
|
+
releaseRate: number;
|
|
54
|
+
exponentialCounter: number;
|
|
55
|
+
exponentialCounterPeriod: number;
|
|
56
|
+
reset(): void;
|
|
57
|
+
}
|
|
58
|
+
export declare class SoundCard implements IO {
|
|
59
|
+
raiseIRQ: () => void;
|
|
60
|
+
raiseNMI: () => void;
|
|
61
|
+
/** Callback to push audio samples to the host emulator */
|
|
62
|
+
pushSamples?: (samples: Float32Array) => void;
|
|
63
|
+
/** Raw register file (write-only from CPU perspective, except reads at $19-$1C) */
|
|
64
|
+
private registers;
|
|
65
|
+
/** Three oscillator voices */
|
|
66
|
+
private voices;
|
|
67
|
+
/** Filter state */
|
|
68
|
+
private filterCutoff;
|
|
69
|
+
private filterResonance;
|
|
70
|
+
private filterRouting;
|
|
71
|
+
private filterMode;
|
|
72
|
+
private masterVolume;
|
|
73
|
+
/** Filter integrator state (continuous) */
|
|
74
|
+
private filterLP;
|
|
75
|
+
private filterBP;
|
|
76
|
+
private filterHP;
|
|
77
|
+
/** Cycle accumulator for sample rate conversion */
|
|
78
|
+
private cycleAccumulator;
|
|
79
|
+
/** Target audio sample rate */
|
|
80
|
+
sampleRate: number;
|
|
81
|
+
/** SID clock rate */
|
|
82
|
+
sidClock: number;
|
|
83
|
+
/** Internal sample buffer for pushing to host */
|
|
84
|
+
private sampleBuffer;
|
|
85
|
+
private sampleBufferIndex;
|
|
86
|
+
read(address: number): number;
|
|
87
|
+
write(address: number, data: number): void;
|
|
88
|
+
tick(frequency: number): void;
|
|
89
|
+
reset(coldStart: boolean): void;
|
|
90
|
+
private updateVoiceRegister;
|
|
91
|
+
private updateGlobalRegister;
|
|
92
|
+
private clockOneCycle;
|
|
93
|
+
private clockOscillator;
|
|
94
|
+
private generateWaveform;
|
|
95
|
+
private clockEnvelope;
|
|
96
|
+
/**
|
|
97
|
+
* Update the exponential counter period based on current envelope level.
|
|
98
|
+
* The real SID uses an exponential curve for decay/release by varying
|
|
99
|
+
* the counter period at specific envelope level thresholds.
|
|
100
|
+
*/
|
|
101
|
+
private updateExponentialPeriod;
|
|
102
|
+
private generateSample;
|
|
103
|
+
/**
|
|
104
|
+
* Convert the 11-bit filter cutoff register value to a frequency in Hz.
|
|
105
|
+
* The SID's cutoff mapping is complex and varies between chips.
|
|
106
|
+
* This approximation covers the usable range (~30 Hz to ~12 kHz).
|
|
107
|
+
*/
|
|
108
|
+
private computeFilterCutoff;
|
|
109
|
+
private flushSampleBuffer;
|
|
110
|
+
/** Get a voice for inspection */
|
|
111
|
+
getVoice(index: number): SIDVoice;
|
|
112
|
+
/** Get current register value */
|
|
113
|
+
getRegister(reg: number): number;
|
|
114
|
+
/** Get current filter cutoff frequency in Hz */
|
|
115
|
+
getFilterCutoffHz(): number;
|
|
116
|
+
/** Get master volume (0-15) */
|
|
117
|
+
getMasterVolume(): number;
|
|
118
|
+
/** Get filter routing bitmask */
|
|
119
|
+
getFilterRouting(): number;
|
|
120
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { IO } from '../IO';
|
|
2
|
+
/**
|
|
3
|
+
* StorageCard - Emulates a Compact Flash card in 8-bit IDE mode
|
|
4
|
+
*
|
|
5
|
+
* Emulates a 128MB CF card with ATA-style register interface.
|
|
6
|
+
* Uses LBA (Logical Block Addressing) for sector access.
|
|
7
|
+
*
|
|
8
|
+
* Register Map (address & 0x07):
|
|
9
|
+
* $00: Data Register (read/write)
|
|
10
|
+
* $01: Error Register (read) / Feature Register (write)
|
|
11
|
+
* $02: Sector Count Register (read/write)
|
|
12
|
+
* $03: LBA0 Register (read/write) - bits 0-7 of LBA
|
|
13
|
+
* $04: LBA1 Register (read/write) - bits 8-15 of LBA
|
|
14
|
+
* $05: LBA2 Register (read/write) - bits 16-23 of LBA
|
|
15
|
+
* $06: LBA3 Register (read/write) - bits 24-27 of LBA + mode bits
|
|
16
|
+
* $07: Status Register (read) / Command Register (write)
|
|
17
|
+
*
|
|
18
|
+
* Supported Commands:
|
|
19
|
+
* 0x20, 0x21: Read Sector(s)
|
|
20
|
+
* 0x30, 0x31: Write Sector(s)
|
|
21
|
+
* 0xC0: Erase Sector
|
|
22
|
+
* 0xEC: Identify Drive
|
|
23
|
+
* 0xEF: Set Features (accepted but not implemented)
|
|
24
|
+
*/
|
|
25
|
+
export declare class StorageCard implements IO {
|
|
26
|
+
private static readonly STORAGE_SIZE;
|
|
27
|
+
private static readonly SECTOR_SIZE;
|
|
28
|
+
private static readonly SECTOR_COUNT;
|
|
29
|
+
private static readonly STATUS_ERR;
|
|
30
|
+
private static readonly STATUS_DRQ;
|
|
31
|
+
private static readonly STATUS_RDY;
|
|
32
|
+
private static readonly ERR_AMNF;
|
|
33
|
+
private static readonly ERR_ABRT;
|
|
34
|
+
private static readonly ERR_IDNF;
|
|
35
|
+
private storage;
|
|
36
|
+
private identity;
|
|
37
|
+
private buffer;
|
|
38
|
+
private bufferIndex;
|
|
39
|
+
private commandDataSize;
|
|
40
|
+
private sectorOffset;
|
|
41
|
+
private error;
|
|
42
|
+
private feature;
|
|
43
|
+
private sectorCount;
|
|
44
|
+
private lba0;
|
|
45
|
+
private lba1;
|
|
46
|
+
private lba2;
|
|
47
|
+
private lba3;
|
|
48
|
+
private status;
|
|
49
|
+
private command;
|
|
50
|
+
private isIdentifying;
|
|
51
|
+
private isTransferring;
|
|
52
|
+
raiseIRQ: () => void;
|
|
53
|
+
raiseNMI: () => void;
|
|
54
|
+
constructor();
|
|
55
|
+
read(address: number): number;
|
|
56
|
+
write(address: number, data: number): void;
|
|
57
|
+
tick(frequency: number): void;
|
|
58
|
+
reset(coldStart: boolean): void;
|
|
59
|
+
private executeCommand;
|
|
60
|
+
private readBuffer;
|
|
61
|
+
private writeBuffer;
|
|
62
|
+
private sectorIndex;
|
|
63
|
+
private sectorValid;
|
|
64
|
+
private generateIdentity;
|
|
65
|
+
/**
|
|
66
|
+
* Load storage data from a file
|
|
67
|
+
* If the file doesn't exist, storage remains empty (initialized to 0x00)
|
|
68
|
+
*/
|
|
69
|
+
loadFromFile(filePath: string): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Save storage data to a file
|
|
72
|
+
*/
|
|
73
|
+
saveToFile(filePath: string): Promise<void>;
|
|
74
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { IO } from '../IO';
|
|
2
|
+
/**
|
|
3
|
+
* TMS9918 Video Display Processor Emulation
|
|
4
|
+
*
|
|
5
|
+
* Port mapping (address bit 0):
|
|
6
|
+
* Even address (bit 0 = 0): VRAM data read/write
|
|
7
|
+
* Odd address (bit 0 = 1): Register/address write / status read
|
|
8
|
+
*
|
|
9
|
+
* Display modes:
|
|
10
|
+
* Graphics I - 32x24 tiles, 8x8 patterns, 1-of-8 color groups
|
|
11
|
+
* Graphics II - 32x24 tiles, 8x8 patterns, per-row color
|
|
12
|
+
* Text - 40x24 tiles, 6x8 patterns, no sprites
|
|
13
|
+
* Multicolor - 32x24 blocks, 4x4 colored cells
|
|
14
|
+
*
|
|
15
|
+
* Output: 256x192 active area centered in a 320x240 RGBA buffer
|
|
16
|
+
*
|
|
17
|
+
* Reference: vrEmuTms9918 by Troy Schrapel
|
|
18
|
+
* https://github.com/visrealm/vrEmuTms9918
|
|
19
|
+
*/
|
|
20
|
+
export declare enum TmsMode {
|
|
21
|
+
GRAPHICS_I = 0,
|
|
22
|
+
GRAPHICS_II = 1,
|
|
23
|
+
TEXT = 2,
|
|
24
|
+
MULTICOLOR = 3
|
|
25
|
+
}
|
|
26
|
+
export declare enum TmsColor {
|
|
27
|
+
TRANSPARENT = 0,
|
|
28
|
+
BLACK = 1,
|
|
29
|
+
MED_GREEN = 2,
|
|
30
|
+
LT_GREEN = 3,
|
|
31
|
+
DK_BLUE = 4,
|
|
32
|
+
LT_BLUE = 5,
|
|
33
|
+
DK_RED = 6,
|
|
34
|
+
CYAN = 7,
|
|
35
|
+
MED_RED = 8,
|
|
36
|
+
LT_RED = 9,
|
|
37
|
+
DK_YELLOW = 10,
|
|
38
|
+
LT_YELLOW = 11,
|
|
39
|
+
DK_GREEN = 12,
|
|
40
|
+
MAGENTA = 13,
|
|
41
|
+
GREY = 14,
|
|
42
|
+
WHITE = 15
|
|
43
|
+
}
|
|
44
|
+
export declare class VideoCard implements IO {
|
|
45
|
+
raiseIRQ: () => void;
|
|
46
|
+
raiseNMI: () => void;
|
|
47
|
+
/** Eight write-only registers */
|
|
48
|
+
private registers;
|
|
49
|
+
/** Status register (read-only from CPU side) */
|
|
50
|
+
private status;
|
|
51
|
+
/** Current VRAM address for CPU access (auto-increments) */
|
|
52
|
+
private currentAddress;
|
|
53
|
+
/** Address / register write stage (0 or 1) */
|
|
54
|
+
private regWriteStage;
|
|
55
|
+
/** Holds first stage byte written to the control port */
|
|
56
|
+
private regWriteStage0Value;
|
|
57
|
+
/** Read-ahead buffer for VRAM reads */
|
|
58
|
+
private readAheadBuffer;
|
|
59
|
+
/** Current display mode (derived from registers) */
|
|
60
|
+
private mode;
|
|
61
|
+
/** 16 KB Video RAM */
|
|
62
|
+
private vram;
|
|
63
|
+
/** Per-pixel sprite collision mask for the current scanline */
|
|
64
|
+
private rowSpriteBits;
|
|
65
|
+
/** Temporary scanline pixel buffer (color palette indices) */
|
|
66
|
+
private scanlinePixels;
|
|
67
|
+
/** 320 × 240 RGBA output buffer for SDL rendering (front buffer – always a complete frame) */
|
|
68
|
+
buffer: Buffer;
|
|
69
|
+
/** Back buffer where scanlines are rendered progressively */
|
|
70
|
+
private backBuffer;
|
|
71
|
+
/** True when a complete frame has been copied to the front buffer */
|
|
72
|
+
frameReady: boolean;
|
|
73
|
+
/** Cycle accumulator for scanline timing */
|
|
74
|
+
private cycleAccumulator;
|
|
75
|
+
/** Current scanline being processed (0 – 261) */
|
|
76
|
+
private currentScanline;
|
|
77
|
+
read(address: number): number;
|
|
78
|
+
write(address: number, data: number): void;
|
|
79
|
+
tick(frequency: number): void;
|
|
80
|
+
reset(_coldStart: boolean): void;
|
|
81
|
+
/**
|
|
82
|
+
* Write to the control (address / register) port.
|
|
83
|
+
* Two-stage write:
|
|
84
|
+
* Stage 0 – latches the low byte (address LSB or register value)
|
|
85
|
+
* Stage 1 – interprets the high byte:
|
|
86
|
+
* bit 7 set → register write (bits 0-2 = register index)
|
|
87
|
+
* bit 7 clear → address set (bit 6: 0 = read, 1 = write)
|
|
88
|
+
*/
|
|
89
|
+
private writeAddr;
|
|
90
|
+
/** Write data to VRAM at the current address (auto-increments) */
|
|
91
|
+
private writeData;
|
|
92
|
+
/**
|
|
93
|
+
* Read the status register.
|
|
94
|
+
* Clears the status flags and resets the write stage.
|
|
95
|
+
*/
|
|
96
|
+
private readStatus;
|
|
97
|
+
/** Read data from VRAM via the read-ahead buffer (auto-increments) */
|
|
98
|
+
private readData;
|
|
99
|
+
private updateMode;
|
|
100
|
+
private nameTableAddr;
|
|
101
|
+
private colorTableAddr;
|
|
102
|
+
private patternTableAddr;
|
|
103
|
+
private spriteAttrTableAddr;
|
|
104
|
+
private spritePatternTableAddr;
|
|
105
|
+
/** Backdrop / border color (low nibble of register 7) */
|
|
106
|
+
private mainBgColor;
|
|
107
|
+
/** Text-mode foreground (high nibble of register 7, transparent → backdrop) */
|
|
108
|
+
private mainFgColor;
|
|
109
|
+
/** Foreground from a color byte (high nibble, transparent → backdrop) */
|
|
110
|
+
private fgColor;
|
|
111
|
+
/** Background from a color byte (low nibble, transparent → backdrop) */
|
|
112
|
+
private bgColor;
|
|
113
|
+
private spriteSize;
|
|
114
|
+
private spriteMag;
|
|
115
|
+
private displayEnabled;
|
|
116
|
+
private processScanline;
|
|
117
|
+
private renderScanline;
|
|
118
|
+
private graphicsIScanLine;
|
|
119
|
+
private graphicsIIScanLine;
|
|
120
|
+
private textScanLine;
|
|
121
|
+
private multicolorScanLine;
|
|
122
|
+
private outputSprites;
|
|
123
|
+
/** Fill entire back buffer with the current backdrop color */
|
|
124
|
+
private fillBackground;
|
|
125
|
+
/** Write a rendered scanline into the back buffer at the correct position */
|
|
126
|
+
private writeScanlineToBuffer;
|
|
127
|
+
/** Read a VDP register value */
|
|
128
|
+
getRegister(reg: number): number;
|
|
129
|
+
/** Write a VDP register value directly (bypasses control-port staging) */
|
|
130
|
+
setRegister(reg: number, value: number): void;
|
|
131
|
+
/** Read a VRAM byte directly (does not affect read-ahead buffer) */
|
|
132
|
+
getVramByte(addr: number): number;
|
|
133
|
+
/** Write a VRAM byte directly (does not affect address pointer) */
|
|
134
|
+
setVramByte(addr: number, value: number): void;
|
|
135
|
+
/** Peek at the status register without clearing it */
|
|
136
|
+
getStatus(): number;
|
|
137
|
+
/** Get the current display mode */
|
|
138
|
+
getMode(): TmsMode;
|
|
139
|
+
/** Get the display-enabled state */
|
|
140
|
+
isDisplayEnabled(): boolean;
|
|
141
|
+
}
|