openmsx.tex 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. % This is a very early openMSX design document. The general ideas remain valid
  2. % today, but some of the details have changed. Especially the parts of the text
  3. % that talk about implementation aspects are outdated.
  4. %
  5. %TODO:
  6. % fully translate in English
  7. % fix many(!) spelling errors
  8. % corrections/additions
  9. \documentclass[11pt, a4paper]{report}
  10. \title{openMSX internals}
  11. %Add your name here
  12. \author{David Heremans}
  13. \author{Wouter Vermaelen}
  14. \begin{document}
  15. \maketitle
  16. \tableofcontents
  17. \chapter{Analysis}
  18. \section{When does a device need to be emulated}
  19. A MSX computer has several components: a CPU, audio and video devices and
  20. many others. All these devices need to be emulated.
  21. In a real machine all devices work in parallel all the time. Of cource an
  22. emulator can only emulate one device at a time. This creates some problems:
  23. First problem, some devices need to emulated \textit{constantly}, e.g.
  24. \begin{itemize}
  25. \item screenrefresh by the VDP
  26. \item soundbuffers for soundcards
  27. \end{itemize}
  28. We can approximate this by emulating those devices very regularly.
  29. Second problem, devices can communicate with each other. When two emulated
  30. devices wants to communicate we must make sure these devices have advanced
  31. equally far in time. Example:
  32. CPU changes by means of I/O-commands the palet settings of the VDP on
  33. time T, therefore the VDP must calculate its screen up until time T before
  34. the IO command is executed. From that moment on the VDP will use the new
  35. palet data when calculating the rest of the screen.
  36. So we need some sort of synchronization mechanism.
  37. \section{Communication in MSX}
  38. \subsection{General principles}
  39. In a MSX communication happens only between the CPU and another device, never
  40. between two non-CPUs. All communication is initiated by the CPU, except for
  41. interupts. Communication happens at a specific moment in time but is not
  42. instantaneous.
  43. \subsection{(Un)predictable communication}
  44. Communication can be either predictable or unpredictable:
  45. \begin{itemize}
  46. \item Predictable communication: Most interupts are predictable, example:
  47. The VDP knows: 'if my settings remain as they are right now, I will
  48. generate a VBLANK interrupt at time T'
  49. \item Unpredicatble communication, examples:
  50. \begin{itemize}
  51. \item CPU executes an IO instruction
  52. \item CPU executes an instruction who reads from or writes to memory (!)
  53. \item an external interrupt (vb RS232)
  54. \end{itemize}
  55. \end{itemize}
  56. Note that \emph{most} unpredictable communication is initiated by the CPU, this is
  57. important for some optimizations (see next chapters).
  58. \subsection{Communication with multiple devices at once}
  59. Important example: memory mappers (IO 0xfc \ldots 0xff).
  60. Writing is not a problem but reading is, since the result is not defined in the MSX
  61. standard. But some programs expect a certain behaviour\ldots
  62. \subsection {Non-standard behaviour}
  63. A device its memory is addressed by slot and address signals. Normally a devices
  64. should check both these signals. Some devices (e.g.: ????) ignore slot-select
  65. and respond whenever they see their own address.
  66. \chapter{Design}
  67. \section{Communication}
  68. \subsection{Initiated by the CPU}
  69. The CPU can read or write to a memory location or to an I/O port. The
  70. communication happens at a specific moment in time and might take a (short) time
  71. to complete. So besides to obvious parameters (adres/port, value) and return
  72. values (byte read), we also need to pass and return a timestamp.
  73. \subsection{Initiated by other device (interupts)}
  74. \subsubsection{General}
  75. All interupts must be planned beforehand (by synchronization points, see next
  76. section). This is easy for predictable interupts, but impossible for
  77. unpredictable interupts.
  78. When a device raises an interupt it must pass control to the emulated CPU before
  79. it can further advance in time.
  80. \subsubsection{Unpredictable interupts}
  81. There are two possible solutions:
  82. \label{unpredic-irq}
  83. \begin{enumerate}
  84. \item Polling.
  85. %%
  86. Bij deze oplossing zetten we zelf de SP om de bv 50 HZ T-states.
  87. Blijven we bij het voorbeeld van de RS232 dan betekend dat er om de 0.02 seconden
  88. gevraagd gaat worden of er een byte klaar staat. Als dit het geval is dan kan de
  89. ge-emuleerde RS232 zijn volgende SP plaatsten binnen bv 100 T-States en deze tijd
  90. langzaam laten toenemen als blijkt dat er geen byte meer ontvangen is.
  91. Methode heeft het voordeel dat je zelf een vorm van snelheid begrenzing kunt
  92. inbouwen door een apparaat steeds maar een lage hoeveelheid interrupts te laten
  93. genereren. Tevens zou op deze manier het mogelijk eenvoudiger zijn om bv een
  94. RS232 connectie te faken aan de hand van een file met een 'opgenomen' connectie.
  95. Bij beide methodes moet het device er voor zorgen dat het er rekening mee houd
  96. dat een geregistreerd SP wel eens tijdens een DI zou kunnen vallen en dat daarom
  97. een gesette interrupt niet noodzakelijker wijze word afgehandeld. Dit wil
  98. zeggen, er kunnen bv drie SP geregistreerd worden die elk een setInterrupt()
  99. uitvoeren maar dat slechts na de derde setInterrupt de CPU deze pas afhandeld!.
  100. \item Converting van Guest OS interrupts naar MSX interrupts
  101. Het zou ook mogelijk zijn om als het guest OS een interrupt krijgt dat deze kan
  102. ingelast worden in de datastructuur met de SP, aangezien deze al in staat is om
  103. SP's tussen te lassen in de bestaande structuur zou dit geen probleem zijn.
  104. \end{enumerate}
  105. \subsection{Communication with multiple devices at once}
  106. Most important example: memory mappers (IO 0xfc \ldots 0xff)
  107. %%
  108. For this kind of behaviour the most usefull principle would be a master-slave approach
  109. In het geval van memmorymappers heb ik dit als volgt reeds geprogrammeert:
  110. \begin{itemize}
  111. \item De classe MSXMemMapDriver(=master) registreerd zich bij het MSXMotherboard voor de
  112. poorten OxFC .. 0xFF.
  113. \item De Driver houd per slot/sublsot bij wat de mappergrootte is. Tijdens de initfase
  114. vervangt de MapperDriver alle verwijzingen naar hem in de slotstructuur door verwijzingen
  115. naar verschillende MemMapSlaves.
  116. \item Bij OUT activiteit zal de driver de gepaste slaves vertellen dat ze nu data moeten
  117. lezen/schrijven op andere plaatsen in mapper van hun main-/sub-slot combinatie.
  118. \item Bij IN activiteit is het de mapper die de waarden kent en de juiste value aan de
  119. CPU terug geeft. Op deze manier is het simpel om de pull-up van de Turbo-R te
  120. emuleren, of de conflicten van het lezen van meerdere mappers in de driver te
  121. implementeren.
  122. \end{itemize}
  123. %%
  124. Dit idee ga ik waarschijnlijk ook gebruiken bij de CPU, op die manier heb je een
  125. CPU-master en twee CPU-slaves bij de turbo R. De master zal zich dan ook registreren
  126. bij het MotherBoard om zo op de CPU-switch poort- van de Turbo-R te kunnen luisteren
  127. en de juiste taakdelegatie te kunnen doen.
  128. \section{Synchronization}
  129. \subsection{Time-active and time-passive devices}
  130. \subsubsection{Time-active devices}
  131. This are devices that can request to be emulated at a future moment in time,
  132. such a moment is called a \emph{synchronization point}. The reasons for such a request
  133. are:
  134. \begin{itemize}
  135. \item Some devices need to be emulated regularly, e.g.:
  136. \begin{itemize}
  137. %%
  138. \item VDP moet regelmatig zorgen voor beeldopbouw
  139. \item soundchips moeten regelmatig nieuwe samples klaarzetten
  140. \end{itemize}
  141. \item Some devices can raise an IRQ, these IRQ must be announced beforehand
  142. with a synchronization point. This is easy for predictable IRQ's, but must be
  143. estimated for unpreditable IRQ's (see section \ref{unpredic-irq}).
  144. \end{itemize}
  145. \subsubsection{Time-passive devices}
  146. These devices can be emulated at the moment the CPU tries to communicate with
  147. it. Example:
  148. \begin{itemize}
  149. \item Keyboard
  150. \item RAM/ROM
  151. \end{itemize}
  152. \subsection{Scheduler algorithm}
  153. A few rules:
  154. \begin{enumerate}
  155. \item %%
  156. geen enkel device mag in uitvoering 'voorliggen' op de CPU.
  157. Dit omdat praktisch alle onvoorspelbare communicate (IO en memory) uitgaat
  158. van de CPU. Als een device op een bepaald moment voorligt op de CPU en als
  159. nadien de CPU tijdens zijn 'inhaalbeweging' communiceerd met dat device, ...
  160. ja dan klopt er iets niet.
  161. \item %%
  162. elk device moet zijn eerstvolgende synchronisatie punt
  163. aan de scheduler melden.
  164. \end{enumerate}
  165. Now the algorithm:
  166. \begin{enumerate}
  167. %%
  168. \item De scheduler neemt het vroegste synchronisatie punt, en \textit{probeert}
  169. de CPU tot op dat punt te brengen.
  170. \item Als dit lukt
  171. \begin{enumerate}
  172. %%
  173. \item \label{alg:ok}
  174. dan wordt ook het device dat dit synchronisatie punt had gezet tot
  175. hier gebracht (dit lukt altijd)
  176. \item \label{alg:nok}
  177. als het niet lukt (CPU heeft een IO of een memory instructie
  178. uitgevoerd). Dan wordt eerst het device waarmee gecommuniceerd
  179. werd bijgebracht (nu niet volledig tot aan synchronisatie punt, maar tot
  180. waar de CPU gekomen was) en dan wordt pas de IO of memory opdracht
  181. uitgevoerd. Het zou kunnen dat het device een (nieuw, vroeger)
  182. synchronisatie punt heeft gezet, dus het algoritme begint weer van
  183. vanvoor.
  184. \end{enumerate}
  185. \end{enumerate}
  186. Note a device may not register a new SP that comes in time before the CPU's
  187. current time. (Step \ref{alg:ok}, \ref{alg:nok})
  188. \chapter{Implementation}
  189. \section{Timetracking}
  190. Every device has its own native clock frequency, for the emulation of one
  191. particular device it is easy to work with native T-states. But for the
  192. global emulator it is easier to work with some least-common-multiple frequency.
  193. The class 'Emutime' offers such an interface: devices work with native T-states,
  194. but they are internally converted to global emulator T-states.
  195. All operations involving time should go through this class.
  196. \section{The implementation of the scheduler in openMSX}
  197. The MSXScheduler will keep track of the T-states. This 'scheduler'-devices
  198. is 'owned' by the MSXMotherBoard. The MSXScheduler is a generic object
  199. that can be reused by other devices. If for example someone would like to
  200. make the VDP as a collection of internal devices each withs it's own
  201. function and needs some internal scheduler ...
  202. \subsection{Scheduler and timetracking}
  203. The MSXScheduler keeps a simple list for timescheduling. This is a simple
  204. list of the next SP and the device that has set this SP. The list is
  205. ordered so that the first element always is the first SP that has to
  206. reached. The scheduler will follow this simple procedure:
  207. \begin{enumerate}
  208. \item Set the target T-State of the cpu to the first SP in the list.
  209. \item Let the CPU execute until the target T-state.
  210. \item Get the device from the first SP in the list and let it reach its
  211. T-state.
  212. \item Remove the first elemnt from the list
  213. \item Goto step 1
  214. \end{enumerate}
  215. Attention. The first element of the list in step 1 could be different from
  216. the first element in step 3. This is because of the fact that during step
  217. 2 the device can do some I/O to other device who can set a new SP that has
  218. a smaller value then the set target T-state. For example, enabling
  219. line-interrupts from the VDP could change the first SP to be reached. For
  220. this reasson the scheduler, when inserting a list element before the first
  221. element, will call the setTargetTStates of the CPU. this will garuantee
  222. that the CPU will execute until the first SP element of the list.
  223. Note 1: To get the extra time-info during communication between CPU and
  224. devices the calls (readMem,writeMem,readIO and writeIO) pas as their
  225. latest parameter the current T-state of the CPU. This makes it possible
  226. for devices to "catch up" with the processor if needed. So the graph of
  227. Wouter could be implicetly included in the emulation by passing
  228. the current time around during device communication.
  229. Note 2: Also non-msx devices could be integrated in this list. for example
  230. the routine that draw the actual screen on your PC could be called from
  231. the scheduler. Also you could make a sort of dummy device that would be
  232. called each 1/50 second and would block emulation until there is really
  233. 1/50 second past between it's previous execution and the current one.
  234. \subsection{Device Sync points}
  235. Since each device has its own natural clockfrequency the scheduler will
  236. run at the speed of the lowest common multiple. In case of an MSX2 the CPU
  237. is at 3.5 MHz, the VDP runs at 21 MHz. Therefore the scheduler runs at 21
  238. MHz.
  239. However we could make a MSXDevice that uses a clockfrequency of 42 MHz. In
  240. this case the scheduler will need run at 42 MHz, however the CPU still
  241. needs to run at 3.5 MHz and not at 7 MHz. To overcomme this problem the
  242. scheduler has a method called getTimeMultiplier(nativeFreq). You can pass
  243. your native frequency and it will tell you by how much you need to
  244. multiply the devices native T-states to get to the frequency of the
  245. scheduler.
  246. In the first case this would result in the value 6, in the 42MHz case this
  247. would return 12. If you ask the scheduler for the current T-state you will
  248. need to divide them by this value to get to the native T-state value
  249. The most used SP setting is from "now" until x T-states later. This can be
  250. achieved using the method setLaterSP(getTimeMultiplier(nativeFreq) *
  251. numberOfNativeTStatesLater , this ).
  252. \section{For interrupts itself}
  253. If a device generates an interrupt it should call the
  254. MSXMotherboard.raiseIRQ() method. when a device doesn't activate
  255. the interrupt line any longer it will call the
  256. MSXMotherboard.lowerIRQ(). The emulate CPU will after each
  257. instruction check the IRQ-line and respond accordingly (EI/DI,
  258. interruptmode,...). In the Motherboard the IRQ-line will be
  259. implemented using a counter, if irq-counter is zero then no
  260. interrupts otherwise the irq-counter should contain the number of
  261. devices that are generating an IRQ.
  262. \emph{A device isn't allowed to call the raise(lower)irq twice in a row!!}
  263. The MSXDevice classe contains a standard method setInterrupt() and
  264. resetInterrupt() which take care of this restriction and call the
  265. MSXMotherBoard if needed.
  266. We could make it mandatory to set the interrupt as an SP. In this way we
  267. would only need to test if the IRQ-line is active each time we call the
  268. CPU from the scheduler. However for most devices this wouldn't change
  269. anything, they still need to emulate up until the SP point and call the
  270. raiseIRQ(). Any comments ??
  271. \chapter{Implementing devices for openMSX}
  272. Before you start implementing a new device for openMSX make sure you have read and understand the way openMSX handles eumTime. If your device is able to generate an IRQ you must follow the rules described above.
  273. \section{The life cycle of an MSXDevice}
  274. \subsection{In the beginning}
  275. In the beginning there was nothing....
  276. And then a lot of things happened and after a few billion yeas it all resulted in openMSX. And internally in openMSX also a lot of things need to happen before you get a running emulator.
  277. This is what happens before a device becomes aware of its existance.
  278. \begin{itemize}
  279. \item The main loop creates an MSXConfig object and let it parse a configuration file.
  280. \item A MSXMotherBoard is create, this object can be viewed as the bus of the MSX and keep tracks of all the devices and slot layouts.
  281. \item The devicefactory creates the objects that are indicate by the MSXConfig object. It loops trough the objects that are summed up in the configuration, each time creating an object of the indicated class and adding to the deviceslist of the motherboard. Each MSXDevice also gets a pointer set towards it personal configuration object. For people who need a more vivid description of what has happended up until now: We just create all the needed chips and PCB's and tossed them all on a big pile.
  282. \end{itemize}
  283. The life cycle of an MSXdevice follows the following basic steps
  284. \begin{itemize}
  285. \item Object is instantiated
  286. \item The device reads its configuration and prepares itself to run
  287. \item The device waits for calls made by the scheduler/CPU.
  288. \item When destroyed the object nicely releases it sources.
  289. \end{itemize}
  290. \subsection{Instantiation}
  291. The life cycle starts with the birth of the object.
  292. During this constructor fase the MSXDevice can set some of its internal parameters to default values. The general rule being that this initialisation must be fail-proof. Meaning that during this initialization we can not try to malloc some memory or read a file or something else that could go hay-wire. By following this simple rule we can safely asume that the device factory can contruct all of the desired devices with out bailing out.
  293. If we need to bail out this will result in the termination of the entire emulator. If for instance the total system memory isn't enough to even create an object.
  294. \subsection{Initialization}
  295. Alsmost imediately after the device is instantiated, the first method that will be called is the setConfigDevice(MSXConfig::Device*). This will allow the MSXDevice to read its own configuration during the following initialization. Remember that the instantiation should be simple and fail-safe. Now it is time to begin the more dangerous internal set up. At this point all needed devices are instantiated, only there configuration isn't complete yet. Now the motherboard start calling the init() method of all of it's known devices.
  296. Know you can try to read files, allocated memory and (since all devices are existing) trying some initial communication with other MSXDevices. Be aware howver that this device itself may not have yet been initialized.
  297. During the initialisation the device should register its place in the slot structure with the MSXMotherBoard. Also registering I/O ports is done at this point.
  298. One of the reasons to have this method seperate from the instatiation is the fact that later on we could decide that each device should be able to be re-initialized during run time. It would be advicable to write your code so that this is possible. For pointers this would mean that the contructor makes them NULL pointers and that the init should check if the pointers is empty before trying the malloc, and constructs such a like.
  299. \subsection{Start}
  300. One of the first things the device should do in his start method is asking the motherboard for the actual emutime. Remember that it should be possible to insert a device during the emulation. So it isn't smart to assume that the start method will be called when emutime is still zero. This is ofcourse only needed if the device uses time \^\_\^
  301. During this start the device sould also set it's synchronisation points if needed. ex. a VDP should register it's first following interrupt.
  302. \subsection{Stop}
  303. \subsection{Reset}
  304. Remark 1 : creating of MSXDevices
  305. ---------------------------------
  306. the sequence in which MSXDevices are used is as follows
  307. --if <slotted> are embeded in the <device>-block this device will be visible in the slotstructure of the emulated MSX.Multiple <slotted>blocks are possible f.e. a simple 64KB RAM expansion is visible in one slot,one subslot and the 4 pages of it.
  308. --the< parameter> block is parsed and every key,value pair is passed to the setParameter methode. This method is used to set internal parameters of the MSXDevice, f.e. the amount of vram could be passed to a VDPdevice, or the number of mapper pages for a MemMapper.
  309. -once athe configuration file is parsed and all devices created, all devices in the deviceslist have their init() method called. In the init() method the devices can allocate desired memmory, register I/O ports using the register\_IO\_Ini/IO\_Out method of the MSXMotherBoard.
  310. -at last the virtual MSX is started, the motherboard sends a reeset signal to all devices and starts emulating, if the user requests a reset, the reset method is called for all devices and the MSX comenses al over. The reset method is used for warm-boot, so for example memory should NOT be cleared from most memory devices.
  311. \end{document}