-
Notifications
You must be signed in to change notification settings - Fork 66
/
Copy pathdma.ts
186 lines (160 loc) · 7.42 KB
/
dma.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
import { Cpu } from '../cpu/index';
import { Memory } from './memory';
import { eightBitLoadFromGBMemoryWithTraps, eightBitLoadFromGBMemory } from './load';
import { eightBitStoreIntoGBMemoryWithTraps, eightBitStoreIntoGBMemory } from './store';
import { concatenateBytes, checkBitOnByte, setBitOnByte, resetBitOnByte } from '../helpers/index';
// Inlined because closure compiler inlines
export function initializeDma(): void {
if (Cpu.GBCEnabled) {
// GBC DMA
eightBitStoreIntoGBMemory(0xff51, 0xff);
eightBitStoreIntoGBMemory(0xff52, 0xff);
eightBitStoreIntoGBMemory(0xff53, 0xff);
eightBitStoreIntoGBMemory(0xff54, 0xff);
eightBitStoreIntoGBMemory(0xff55, 0xff);
} else {
// GB DMA
eightBitStoreIntoGBMemory(0xff51, 0xff);
eightBitStoreIntoGBMemory(0xff52, 0xff);
eightBitStoreIntoGBMemory(0xff53, 0xff);
eightBitStoreIntoGBMemory(0xff54, 0xff);
eightBitStoreIntoGBMemory(0xff55, 0xff);
}
}
// Inlined because closure compiler inlines
export function startDmaTransfer(sourceAddressOffset: i32): void {
let sourceAddress = sourceAddressOffset << 8;
for (let i = 0; i <= 0x9f; ++i) {
let spriteInformationByte = eightBitLoadFromGBMemory(sourceAddress + i);
let spriteInformationAddress = Memory.spriteInformationTableLocation + i;
eightBitStoreIntoGBMemory(spriteInformationAddress, spriteInformationByte);
}
// TCAGBD: This copy (DMA) needs 160 × 4 + 4 clocks to complete in both double speed and single speeds modes
// Increment all of our Cycle coiunters in ../cpu/opcodes
Memory.DMACycles = 644;
}
// https://gist.github.com/drhelius/3394856
// http://bgb.bircd.org/pandocs.htm
// Inlined because closure compiler inlines
export function startHdmaTransfer(hdmaTriggerByteToBeWritten: i32): void {
// Check if we are Gbc
if (!Cpu.GBCEnabled) {
return;
}
// Check if we are trying to terminate an already active HBLANK HDMA
if (Memory.isHblankHdmaActive && !checkBitOnByte(7, hdmaTriggerByteToBeWritten)) {
// Don't reset anything, just set bit 7 to 1 on the trigger byte
Memory.isHblankHdmaActive = false;
let hdmaTriggerByte = eightBitLoadFromGBMemory(Memory.memoryLocationHdmaTrigger);
eightBitStoreIntoGBMemory(Memory.memoryLocationHdmaTrigger, setBitOnByte(7, hdmaTriggerByte));
return;
}
// Get our source and destination for the HDMA
let hdmaSource = getHdmaSourceFromMemory();
let hdmaDestination = getHdmaDestinationFromMemory();
// Get the length from the trigger
// Lower 7 bits, Add 1, times 16
// https://gist.github.com/drhelius/3394856
let transferLength = resetBitOnByte(7, hdmaTriggerByteToBeWritten);
transferLength = (transferLength + 1) << 4;
// Get bit 7 of the trigger for the HDMA type
if (checkBitOnByte(7, hdmaTriggerByteToBeWritten)) {
// H-Blank DMA
Memory.isHblankHdmaActive = true;
Memory.hblankHdmaTransferLengthRemaining = transferLength;
Memory.hblankHdmaSource = hdmaSource;
Memory.hblankHdmaDestination = hdmaDestination;
// This will be handled in updateHblankHdma()
// Since we return false in write traps, we need to now write the byte
// Be sure to reset bit 7, to show that the hdma is active
eightBitStoreIntoGBMemory(Memory.memoryLocationHdmaTrigger, resetBitOnByte(7, hdmaTriggerByteToBeWritten));
} else {
// General DMA
hdmaTransfer(hdmaSource, hdmaDestination, transferLength);
// Stop the DMA
eightBitStoreIntoGBMemory(Memory.memoryLocationHdmaTrigger, 0xff);
}
}
// Inlined because closure compiler inlines
export function updateHblankHdma(): void {
if (!Memory.isHblankHdmaActive) {
return;
}
// Get our amount of bytes to transfer (Only 0x10 bytes at a time)
let bytesToTransfer = 0x10;
let hblankHdmaTransferLengthRemaining = Memory.hblankHdmaTransferLengthRemaining;
if (hblankHdmaTransferLengthRemaining < bytesToTransfer) {
// Set to the difference
bytesToTransfer = hblankHdmaTransferLengthRemaining;
}
// Do the transfer (Only 0x10 bytes at a time)
hdmaTransfer(Memory.hblankHdmaSource, Memory.hblankHdmaDestination, bytesToTransfer);
// Update our source and destination
Memory.hblankHdmaSource += bytesToTransfer;
Memory.hblankHdmaDestination += bytesToTransfer;
hblankHdmaTransferLengthRemaining -= bytesToTransfer;
Memory.hblankHdmaTransferLengthRemaining = hblankHdmaTransferLengthRemaining;
let memoryLocationHdmaTrigger = Memory.memoryLocationHdmaTrigger;
if (hblankHdmaTransferLengthRemaining <= 0) {
// End the transfer
Memory.isHblankHdmaActive = false;
// Need to clear the HDMA with 0xFF, which sets bit 7 to 1 to show the HDMA has ended
eightBitStoreIntoGBMemory(memoryLocationHdmaTrigger, 0xff);
} else {
// Set our new transfer length, make sure it is in the weird format,
// and make sure bit 7 is 0, to show that the HDMA is Active
let remainingTransferLength = hblankHdmaTransferLengthRemaining;
let transferLengthAsByte = (remainingTransferLength >> 4) - 1;
eightBitStoreIntoGBMemory(memoryLocationHdmaTrigger, resetBitOnByte(7, transferLengthAsByte));
}
}
// Simple Function to transfer the bytes from a destination to a source for a general pourpose or Hblank HDMA
function hdmaTransfer(hdmaSource: i32, hdmaDestination: i32, transferLength: i32): void {
for (let i = 0; i < transferLength; ++i) {
let sourceByte = eightBitLoadFromGBMemoryWithTraps(hdmaSource + i);
// get the hdmaDestination with wrapping
// See issue #61: https://github.com/torch2424/wasmBoy/issues/61
let hdmaDestinationWithWrapping = hdmaDestination + i;
while (hdmaDestinationWithWrapping > 0x9fff) {
// Simply clear the top 3 bits
hdmaDestinationWithWrapping -= 0x2000;
}
eightBitStoreIntoGBMemoryWithTraps(hdmaDestinationWithWrapping, sourceByte);
}
// Set our Cycles used for the HDMA
// Since DMA in GBC Double Speed Mode takes 80 micro seconds,
// And HDMA takes 8 micro seconds per 0x10 bytes in GBC Double Speed mode (and GBC Normal Mode)
// Will assume (644 / 10) cycles for GBC Double Speed Mode,
// and (644 / 10 / 2) for GBC Normal Mode
let hdmaCycles = 32 << (<i32>Cpu.GBCDoubleSpeed);
hdmaCycles = hdmaCycles * (transferLength >> 4);
Memory.DMACycles += hdmaCycles;
}
// Function to get our HDMA Source
// Follows the poan docs
// Inlined because closure compiler inlines
function getHdmaSourceFromMemory(): i32 {
// Get our source for the HDMA
let hdmaSourceHigh = eightBitLoadFromGBMemory(Memory.memoryLocationHdmaSourceHigh);
let hdmaSourceLow = eightBitLoadFromGBMemory(Memory.memoryLocationHdmaSourceLow);
let hdmaSource = concatenateBytes(hdmaSourceHigh, hdmaSourceLow);
// And off the appopriate bits for the source and destination
// And off the bottom 4 bits
hdmaSource = hdmaSource & 0xfff0;
return hdmaSource;
}
// Function to get our HDMA Destination
// Follows the poan docs
// Inlined because closure compiler inlines
function getHdmaDestinationFromMemory(): i32 {
let hdmaDestinationHigh = eightBitLoadFromGBMemory(Memory.memoryLocationHdmaDestinationHigh);
let hdmaDestinationLow = eightBitLoadFromGBMemory(Memory.memoryLocationHdmaDestinationLow);
let hdmaDestination = concatenateBytes(hdmaDestinationHigh, hdmaDestinationLow);
// Can only be in VRAM, 0x8000 -> 0x9FF0
// Pan docs says to knock off upper 3 bits, and lower 4 bits
// Which gives us: 0001111111110000 or 0x1FF0
// Meaning we must add 0x8000
hdmaDestination = hdmaDestination & 0x1ff0;
hdmaDestination += Memory.videoRamLocation;
return hdmaDestination;
}