1//============================================================================
2//
3// MM MM 6666 555555 0000 2222
4// MMMM MMMM 66 66 55 00 00 22 22
5// MM MMM MM 66 55 00 00 22
6// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator"
7// MM MM 66 66 55 00 00 22
8// MM MM 66 66 55 55 00 00 22
9// MM MM 6666 5555 0000 222222
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#include <cassert>
19#include <iostream>
20
21#include "Device.hxx"
22#include "M6502.hxx"
23#include "M6532.hxx"
24#include "TIA.hxx"
25#include "Cart.hxx"
26#include "TimerManager.hxx"
27#include "System.hxx"
28
29// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
30System::System(Random& random, M6502& m6502, M6532& m6532,
31 TIA& mTIA, Cartridge& mCart)
32 : myRandom(random),
33 myM6502(m6502),
34 myM6532(m6532),
35 myTIA(mTIA),
36 myCart(mCart),
37 myCycles(0),
38 myDataBusState(0),
39 myDataBusLocked(false),
40 mySystemInAutodetect(false)
41{
42 // Initialize page access table
43 PageAccess access(&myNullDevice, System::PageAccessType::READ);
44 myPageAccessTable.fill(access);
45 myPageIsDirtyTable.fill(false);
46
47 // Bus starts out unlocked (in other words, peek() changes myDataBusState)
48 myDataBusLocked = false;
49}
50
51// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
52void System::initialize()
53{
54 // Install all devices
55 myM6532.install(*this);
56 myTIA.install(*this);
57 myCart.install(*this);
58 myM6502.install(*this); // Must always be installed last
59}
60
61// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
62void System::reset(bool autodetect)
63{
64 // Provide hint to devices that autodetection is active (or not)
65 mySystemInAutodetect = autodetect;
66
67 // Reset all devices
68 myCycles = 0; // Must be done first (the reset() methods may use its value)
69 myM6532.reset();
70 myTIA.reset();
71 myCart.reset();
72 myM6502.reset(); // Must always be reset last
73
74 // There are no dirty pages upon startup
75 clearDirtyPages();
76}
77
78// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
79void System::consoleChanged(ConsoleTiming timing)
80{
81 myM6532.consoleChanged(timing);
82 myTIA.consoleChanged(timing);
83 myCart.consoleChanged(timing);
84}
85
86// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
87bool System::isPageDirty(uInt16 start_addr, uInt16 end_addr) const
88{
89 uInt16 start_page = (start_addr & ADDRESS_MASK) >> PAGE_SHIFT;
90 uInt16 end_page = (end_addr & ADDRESS_MASK) >> PAGE_SHIFT;
91
92 for(uInt16 page = start_page; page <= end_page; ++page)
93 if(myPageIsDirtyTable[page])
94 return true;
95
96 return false;
97}
98
99// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
100void System::clearDirtyPages()
101{
102 myPageIsDirtyTable.fill(false);
103}
104
105// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
106uInt8 System::peek(uInt16 addr, uInt8 flags)
107{
108 const PageAccess& access = getPageAccess(addr);
109
110#ifdef DEBUGGER_SUPPORT
111 // Set access type
112 if(access.codeAccessBase)
113 *(access.codeAccessBase + (addr & PAGE_MASK)) |= flags;
114 else
115 access.device->setAccessFlags(addr, flags);
116#endif
117
118 // See if this page uses direct accessing or not
119 uInt8 result;
120 if(access.directPeekBase)
121 result = *(access.directPeekBase + (addr & PAGE_MASK));
122 else
123 result = access.device->peek(addr);
124
125#ifdef DEBUGGER_SUPPORT
126 if(!myDataBusLocked)
127#endif
128 myDataBusState = result;
129
130 return result;
131}
132
133// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
134void System::poke(uInt16 addr, uInt8 value, uInt8 flags)
135{
136 uInt16 page = (addr & ADDRESS_MASK) >> PAGE_SHIFT;
137 const PageAccess& access = myPageAccessTable[page];
138
139#ifdef DEBUGGER_SUPPORT
140 // Set access type
141 if (access.codeAccessBase)
142 *(access.codeAccessBase + (addr & PAGE_MASK)) |= flags;
143 else
144 access.device->setAccessFlags(addr, flags);
145#endif
146
147 // See if this page uses direct accessing or not
148 if(access.directPokeBase)
149 {
150 // Since we have direct access to this poke, we can dirty its page
151 *(access.directPokeBase + (addr & PAGE_MASK)) = value;
152 myPageIsDirtyTable[page] = true;
153 }
154 else
155 {
156 // The specific device informs us if the poke succeeded
157 myPageIsDirtyTable[page] = access.device->poke(addr, value);
158 }
159
160#ifdef DEBUGGER_SUPPORT
161 if(!myDataBusLocked)
162#endif
163 myDataBusState = value;
164}
165
166// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
167uInt8 System::getAccessFlags(uInt16 addr) const
168{
169#ifdef DEBUGGER_SUPPORT
170 const PageAccess& access = getPageAccess(addr);
171
172 if(access.codeAccessBase)
173 return *(access.codeAccessBase + (addr & PAGE_MASK));
174 else
175 return access.device->getAccessFlags(addr);
176#else
177 return 0;
178#endif
179}
180
181// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
182void System::setAccessFlags(uInt16 addr, uInt8 flags)
183{
184#ifdef DEBUGGER_SUPPORT
185 const PageAccess& access = getPageAccess(addr);
186
187 if(access.codeAccessBase)
188 *(access.codeAccessBase + (addr & PAGE_MASK)) |= flags;
189 else
190 access.device->setAccessFlags(addr, flags);
191#endif
192}
193
194// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
195bool System::save(Serializer& out) const
196{
197 try
198 {
199 out.putLong(myCycles);
200 out.putByte(myDataBusState);
201
202 // Save the state of each device
203 if(!myM6502.save(out))
204 return false;
205 if(!myM6532.save(out))
206 return false;
207 if(!myTIA.save(out))
208 return false;
209 if(!myCart.save(out))
210 return false;
211 if(!randGenerator().save(out))
212 return false;
213 }
214 catch(...)
215 {
216 cerr << "ERROR: System::save" << endl;
217 return false;
218 }
219
220 return true;
221}
222
223// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
224bool System::load(Serializer& in)
225{
226 try
227 {
228 myCycles = in.getLong();
229 myDataBusState = in.getByte();
230
231 // Load the state of each device
232 if(!myM6502.load(in))
233 return false;
234 if(!myM6532.load(in))
235 return false;
236 if(!myTIA.load(in))
237 return false;
238 if(!myCart.load(in))
239 return false;
240 if(!randGenerator().load(in))
241 return false;
242 }
243 catch(...)
244 {
245 cerr << "ERROR: System::load" << endl;
246 return false;
247 }
248
249 return true;
250}
251