File: implementation_notes.txt

package info (click to toggle)
fuse-emulator 1.1.1%2Bdfsg1-2
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 11,120 kB
  • ctags: 8,968
  • sloc: ansic: 78,960; sh: 11,228; perl: 3,742; makefile: 1,104; yacc: 236; lex: 140
file content (123 lines) | stat: -rw-r--r-- 5,772 bytes parent folder | download | duplicates (2)
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
Implementation notes for some of Fuse's systems
===============================================

Contents:

1. The main emulation loop
2. The display code
2.1. Building an image of the Spectrum's screen

1. The main emulation loop
==========================

The main emulation loop (the while loop in fuse.c:main) is essentially
fairly simple: it just runs Z80 opcodes until something `interesting'
happens, at which point it deals with the `interesting' thing.

The question here is how do we know when something `interesting' has
occurred: the simple answer is that something interesting occurs when
the `tstates' global variable (which counts tstates since the last
interrupt occurred) reaches `event_next_event'. It should be noted here
that these events are purely a Fuse concept, and not related to any OS
feature.

Perhaps the most obvious type of event which can occur is an
interrupt, which (on the 48K machine anyway) will occur 69888 tstates
after the last interrupt. When each interrupt occurs, the code sets
another interrupt to occur one frame later (the event_add() call in
spectrum.c:spectrum_interrupt). `event_next_event' will then be set by
the code in event.c.

TZX playback is handled in much the same way: an event (of type
EVENT_TYPE_EDGE) is scheduled to occur whenever the signal from the
(emulated) tape changes. That event then toggles the input to the
ULA's EAR bit, and schedules another event to occur whenever the next
edge is due from the tape.

RZX playback cannot be handled in the same way, as it is not known
after how many tstates the interrupt will occur. In this case, the
interrupt is forced when it is due to occur by scheduling an event
from the main Z80 emulation loop (z80/z80_ops.c:z80_do_opcodes) and
then breaking out of the loop.

2. The display code
===================

There are two stages to producing the Spectrum's screen on the
emulating machine's screen: firstly, building an image of the
Spectrum's screen in memory, and then translating that image onto the
emulating machine's screen.

The first of these functions is accomplished by the code in display.c,
whilst the second is fulfilled by each user interface separately,
although generally in ui/<name>/<name>display.c.

2.1. Building an image of the Spectrum's screen
-----------------------------------------------

The function of almost all the code in display.c is to build an image
of the Spectrum's screen in the display.c:display_image array. For the
`normal' (non-Timex) machines, this array has a size 320x240 and each
pixel represents one pixel on the Spectrum's screen (including 32
pixels of left and right border, and 24 pixels of top and bottom
border). For the Timex machines, this array is sized 640x480 to
accommodate the hires modes and each Spectrum pixel is represented by
two vertically adjacent pixels in the array (as the hires modes double
only the horizontal resolution, not the vertical resolution). In both
cases, the values in this array are the Spectrum colours (0 to 15).

Every time the screen memory is written to (and actually changed, as
opposed to the data already there being written back again), three
things occur:

* Fuse works out if it needs to update the display. It does this by
  looking at the region between the current position of the electron
  beam and the position it was in the last time it updated the
  display. If the write affects this 'critical' region, then the
  display_image array is updated with any 'dirty' chunks (see below)
  in the critical region.

  Each run of dirty pixels is noted, and a list of rectangles of such
  pixels is built up by display.c:add_rectangle() and
  display.c:end_line(). At the end of the frame, each of these
  rectangles is passed off to the user-interface specific rendering
  code to be drawn onto the emulating machine's screen.

* The new data is now written to RAM.

* Fuse marks any pixels affected by the write as 'dirty' in the
  display.c:display_is_dirty array. Each bit in each entry represents
  an 8 (non-Timex) or 16 (Timex) pixel chunk of the screen plus border
  which must be updated. The least significant bit represents the
  left-most 8 (16) pixels, the second bit the next 8 (16) and so
  on. (For the rest of this section, I'll take the doubling in pixel
  numbers for Timex machines as read).

  If the `data' area of memory is written to, one 8 pixel chunk is
  marked as `dirty', whilst 64 pixels (in an 8x8 square) are marked as
  dirty if the attributes area is written to. `FLASH'ing characters
  will also cause 8x8 pixel chunks to be marked as dirty every 16
  frames (display.c:display_dirty_flashing()).  The arrays
  display_dirty_xtable and display_dirty_ytable store the address to
  coordinate mappings for the data area of the Spectrum's screen,
  whilst display_dirty_xtable2 and display_dirty_ytable2 serves the
  same function for the attributes area.

2.2. From display_image to the emulating machine's screen
---------------------------------------------------------

At the end of every frame, Fuse calls uidisplay_area repeatedly to get
the user interface to update the emulating machine's screen.

If a user interface is outputting the same number of pixels as in
display_image (320x240 for non-Timex, 640x480 for Timex), this can be
very simple, but user interfaces which implement scaling (either
upwards, or downwards as is necessary for displaying the Timex modes
in a 320x240 mode) may wish to make use of the 'scalers' defined in
ui/scaler: a generalised set of routines for accomplishing this, as
well as various smoothing options and the like (for example,
scanlines). See `scalers.txt' for more information on these.

(FIXME: write scalers.txt)

$Id: implementation_notes.txt 3032 2007-06-28 22:48:54Z zubzero $