-
Notifications
You must be signed in to change notification settings - Fork 20
/
memorymap.go
161 lines (138 loc) · 4.6 KB
/
memorymap.go
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
// This file is part of Gopher2600.
//
// Gopher2600 is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Gopher2600 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
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
package memorymap
// Area represents the different areas of memory.
type Area int
func (a Area) String() string {
switch a {
case TIA:
return "TIA"
case RAM:
return "RAM"
case RIOT:
return "RIOT"
case Cartridge:
return "Cartridge"
}
return "undefined"
}
// The different memory areas in the VCS.
const (
Undefined Area = iota
TIA
RAM
RIOT
Cartridge
)
// The origin and memory top for each area of memory. Checking which area an
// address falls within and forcing the address into the normalised range is
// all handled by the MapAddress() function.
//
// Implementations of the different memory areas may need to drag the address
// down further into the the range of the memory array. This is best done with
// (address^origin) rather than subtraction.
const (
OriginTIA = uint16(0x0000)
MemtopTIA = uint16(0x003f)
OriginRAM = uint16(0x0080)
MemtopRAM = uint16(0x00ff)
OriginRIOT = uint16(0x0280)
MemtopRIOT = uint16(0x0297)
MemtopChipRegisters = MemtopRIOT
OriginCart = uint16(0x1000)
MemtopCart = uint16(0x1fff)
OriginAbsolute = uint16(0x0000)
MemtopAbsolute = uint16(0xffff)
)
// Cartridge memory is mirrored in a number of places in the address space. The
// most useful mirror is the Fxxx mirror which many programmers use when
// writing assembly programs. The following constants are used by the
// disassembly package to reference the disassembly to the Fxxx mirror.
//
// Be extra careful when looping with MemtopCartFxxxMirror because it is at the
// very edge of uint16. Limit detection may need to consider the overflow
// conditions.
const (
OriginCartFxxxMirror = uint16(0xf000)
MemtopCartFxxxMirror = uint16(0xffff)
)
// Memtop is the top most address of memory in the VCS. It is the same as the
// cartridge memtop.
const Memtop = uint16(0x1fff)
// CartridgeBits identifies the bits in an address that are relevant to the
// cartridge address. Useful for discounting those bits that determine the
// cartridge mirror. For example, the following will be true:
//
// 0x1123 & CartridgeBits == 0xf123 & CartridgeBits
//
// Alternatively, the following is an effective way to index an array:
//
// addr := 0xf000
// mem[addr & CartridgeBits] = 0xff
//
// In the example, index zero of the mem array is assigned the value 0xff.
const (
CartridgeBits = OriginCart ^ MemtopCart
)
// The masks to apply to an address to bring any address into the primary
// range. Prefer to use MapAddress() for ease of use.
const (
maskCart = MemtopCart
maskRAM = MemtopRAM
maskWriteTIA = MemtopTIA
maskReadTIA = uint16(0x000f)
maskWriteRIOT = MemtopRIOT
maskReadRIOT = uint16(0x0287)
// read registers specific to the RIOT timer start at 0x284. the two bit of
// the address is masked out leaving 4 addresses mapping to just two
// registers - INTIM and TIMINT
maskReadRIOT_timer = uint16(0x284)
maskReadRIOT_timer_correction = uint16(0x285)
)
// MapAddress translates the address argument from mirror space to primary
// space. Generally, an address should be passed through this function before
// accessing memory.
func MapAddress(address uint16, read bool) (uint16, Area) {
// note that the order of these filters is important
// cartridge addresses
if address&OriginCart == OriginCart {
return address & maskCart, Cartridge
}
// RIOT addresses
if address&OriginRIOT == OriginRIOT {
if read {
if address&maskReadRIOT_timer == maskReadRIOT_timer {
// additional masking for RIOT timer addresses
return address & maskReadRIOT_timer_correction, RIOT
}
return address & maskReadRIOT, RIOT
}
return address & maskWriteRIOT, RIOT
}
// RAM addresses
if address&OriginRAM == OriginRAM {
return address & maskRAM, RAM
}
// everything else is in TIA space
if read {
return address & maskReadTIA, TIA
}
return address & maskWriteTIA, TIA
}
// IsArea returns true if the address is in the specificied area.
func IsArea(address uint16, area Area) bool {
_, a := MapAddress(address, true)
return area == a
}