-
Notifications
You must be signed in to change notification settings - Fork 1
/
mapper4.prg
215 lines (190 loc) · 6.46 KB
/
mapper4.prg
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#define XHB_BITOP // Habilita das operações | & ^^
#include "xhb.ch"
#include "common.ch"
#include "hbclass.ch"
//#define SHOWLOG
CREATE CLASS Mapper4
VAR Cartridge
VAR Console
VAR register INIT 0 // byte
VAR registers INIT {0,0,0,0,0,0,0,0}
VAR prgMode INIT 0 // byte
VAR chrMode INIT 0 // byte
VAR prgOffsets INIT {0,0,0,0}
VAR chrOffsets INIT {0,0,0,0,0,0,0,0}
VAR Reload INIT 0
VAR counter INIT 0
VAR irqEnable INIT .f.
METHOD New(cartridge,console)
METHOD Step()
METHOD Read(address)
METHOD Write(address, value)
METHOD updateOffsets()
METHOD chrBankOffset(index)
METHOD prgBankOffset(index)
METHOD writeIRQEnable(value)
METHOD writeIRQDisable(value)
METHOD writeIRQReload(value)
METHOD writeIRQLatch(value)
METHOD writeProtect(value)
METHOD writeMirror(value)
METHOD writeBankData(value)
METHOD writeBankSelect(value)
METHOD writeRegister(address,value)
METHOD HandleScanLine()
END CLASS
METHOD New(cartridge,console) CLASS Mapper4
::Cartridge := cartridge
::Console := console
::prgOffsets[1]=::prgBankOffset(0)
::prgOffsets[2]=::prgBankOffset(1)
::prgOffsets[3]=::prgBankOffset(-2)
::prgOffsets[4]=::prgBankOffset(-1)
RETURN Self
METHOD Step() CLASS Mapper4
if ::Console:PPU:Cycle != 280 // TODO: this *should* be 260
return
end if
if ::Console:PPU:ScanLine > 239 .and. ::Console:PPU:ScanLine < 261
return
end if
if ::Console:PPU:flagShowBackground == 0 .and. ::Console:PPU:flagShowSprites == 0
return
end if
::HandleScanLine()
RETURN
METHOD HandleScanLine() CLASS Mapper4
if ::counter == 0
::counter = ::reload
else
::counter--
if ::counter == 0 .and. ::irqEnable
::console:CPU:triggerIRQ()
end if
end if
METHOD Read(address) CLASS Mapper4
do case
case address < 0x2000
bank := address / 0x0400
offset := address % 0x0400
return ::cartridge:CHR[::chrOffsets[bank+1]+int(offset)+1]
case address >= 0x8000
address = address - 0x8000
bank := address / 0x2000
offset := address % 0x2000
return ::cartridge:PRG[::prgOffsets[bank+1]+int(offset)+1]
case address >= 0x6000
return ::cartridge:SRAM[((address)-0x6000)+1]
default
? "unhandled mapper4 read at address: ",address
end case
return 0
METHOD Write(address, value) CLASS Mapper4
do case
case address < 0x2000
bank := address / 0x0400
offset := address % 0x0400
::cartridge:CHR[::chrOffsets[bank+1]+int(offset)+1] = value
case address >= 0x8000
::writeRegister(address, value)
case address >= 0x6000
::cartridge:SRAM[(int(address)-0x6000)+1] = value
default
? "unhandled mapper4 write at address: ",address
end case
METHOD writeRegister(address,value) CLASS Mapper4
do case
case address <= 0x9FFF .and. address % 2 == 0
::writeBankSelect(value)
case address <= 0x9FFF .and. address % 2 == 1
::writeBankData(value)
case address <= 0xBFFF .and. address % 2 == 0
::writeMirror(value)
case address <= 0xBFFF .and. address % 2 == 1
::writeProtect(value)
case address <= 0xDFFF .and. address % 2 == 0
::writeIRQLatch(value)
case address <= 0xDFFF .and. address % 2 == 1
::writeIRQReload(value)
case address <= 0xFFFF .and. address % 2 == 0
::writeIRQDisable(value)
case address <= 0xFFFF .and. address % 2 == 1
::writeIRQEnable(value)
end case
METHOD writeBankSelect(value) CLASS Mapper4
::prgMode = ((value >> 6) & 1)
::chrMode = ((value >> 7) & 1)
::register = (value & 7)
::updateOffsets()
METHOD writeBankData(value) CLASS Mapper4
::registers[::register+1] = value
::updateOffsets()
METHOD writeMirror(value) CLASS Mapper4
do case
case (value & 1)=0
::Cartridge:Mirror = 1 // MirrorVertical
case (value & 1)=1
::Cartridge:Mirror = 0 // MirrorHorizontal
end case
METHOD writeProtect(value) CLASS Mapper4
METHOD writeIRQLatch(value) CLASS Mapper4
::reload = value
METHOD writeIRQReload(value) CLASS Mapper4
::counter = 0
METHOD writeIRQDisable(value) CLASS Mapper4
::irqEnable = .f.
METHOD writeIRQEnable(value) CLASS Mapper4
::irqEnable = .t.
METHOD prgBankOffset(index) CLASS Mapper4
if index >= 0x80
index -= 0x100
end if
index %= (len(::cartridge:PRG) / 0x2000)
offset := index * 0x2000
if offset < 0
offset += len(::cartridge:PRG)
end if
return offset
METHOD chrBankOffset(index) CLASS Mapper4
if index >= 0x80
index -= 0x100
end if
index %= len(::cartridge:CHR) / 0x0400
offset := index * 0x0400
if offset < 0
offset += len(::cartridge:CHR)
end if
return offset
METHOD updateOffsets() CLASS Mapper4
do case
case ::prgMode=0
::prgOffsets[1] = ::prgBankOffset(int(::registers[7]))
::prgOffsets[2] = ::prgBankOffset(int(::registers[8]))
::prgOffsets[3] = ::prgBankOffset(-2)
::prgOffsets[4] = ::prgBankOffset(-1)
case ::prgMode=1
::prgOffsets[1] = ::prgBankOffset(-2)
::prgOffsets[2] = ::prgBankOffset(int(::registers[8]))
::prgOffsets[3] = ::prgBankOffset(int(::registers[7]))
::prgOffsets[4] = ::prgBankOffset(-1)
end case
do case
case ::chrMode=0
::chrOffsets[1] = ::chrBankOffset(int((::registers[1] & 0xFE)))
::chrOffsets[2] = ::chrBankOffset(int((::registers[1] | 0x01)))
::chrOffsets[3] = ::chrBankOffset(int((::registers[2] & 0xFE)))
::chrOffsets[4] = ::chrBankOffset(int((::registers[2] | 0x01)))
::chrOffsets[5] = ::chrBankOffset(int(::registers[3]))
::chrOffsets[6] = ::chrBankOffset(int(::registers[4]))
::chrOffsets[7] = ::chrBankOffset(int(::registers[5]))
::chrOffsets[8] = ::chrBankOffset(int(::registers[6]))
case ::chrMode=1
::chrOffsets[1] = ::chrBankOffset(int(::registers[3]))
::chrOffsets[2] = ::chrBankOffset(int(::registers[4]))
::chrOffsets[3] = ::chrBankOffset(int(::registers[5]))
::chrOffsets[4] = ::chrBankOffset(int(::registers[6]))
::chrOffsets[5] = ::chrBankOffset(int((::registers[1] & 0xFE)))
::chrOffsets[6] = ::chrBankOffset(int((::registers[1] | 0x01)))
::chrOffsets[7] = ::chrBankOffset(int((::registers[2] & 0xFE)))
::chrOffsets[8] = ::chrBankOffset(int((::registers[2] | 0x01)))
end case