1 |
BASILISK II TECHNICAL MANUAL |
2 |
============================ |
3 |
|
4 |
0. Table of Contents |
5 |
-------------------- |
6 |
|
7 |
1. Introduction |
8 |
2. Modes of operation |
9 |
3. Memory access |
10 |
4. Calling native routines from 68k mode and vice-versa |
11 |
5. Interrupts |
12 |
6. Parts of Basilisk II |
13 |
7. Porting Basilisk II |
14 |
|
15 |
1. Introduction |
16 |
--------------- |
17 |
|
18 |
Basilisk II can emulate two kind of Macs, depending on the ROM being used: |
19 |
|
20 |
1. A Mac Classic |
21 |
2. A Mac II series computer ("Mac II series" here means all 68020/30/40 |
22 |
based Macs with 32-bit clean ROMs (this excludes the original Mac II, |
23 |
the IIx/IIcx and the SE/030), except PowerBooks; in the following, |
24 |
"Mac II" is used as an abbreviation of "Mac II series computer", as |
25 |
defined above) |
26 |
|
27 |
More precisely spoken, MacOS under Basilisk II behaves like on a Mac Classic |
28 |
or Mac II because, apart from the CPU, the RAM and the ROM, absolutely no Mac |
29 |
hardware is emulated. Rather, Basilisk II provides replacements (usually in |
30 |
the form of MacOS drivers) for the parts of MacOS that access hardware. As |
31 |
there are practically no Mac applications that access hardware directly (this |
32 |
is also due to the fact that the hardware of different Mac models is sometimes |
33 |
as different as, say, the hardware of an Atari ST and an Amiga 500), both the |
34 |
compatibility and speed of this approach are very high. |
35 |
|
36 |
2. Modes of operation |
37 |
--------------------- |
38 |
|
39 |
Basilisk II is designed to run on many different hardware platforms and on |
40 |
many different operating systems. To provide optimal performance under all |
41 |
environments, it can run in four different modes, depending on the features |
42 |
of the underlying environment (the modes are selected with the REAL_ADDRESSING, |
43 |
DIRECT_ADDRESSING and EMULATED_68K defines in "sysdeps.h"): |
44 |
|
45 |
1. Emulated CPU, "virtual" addressing (EMULATED_68K = 1, REAL_ADDRESSING = 0): |
46 |
This mode is designed for non-68k or little-endian systems or systems that |
47 |
don't allow accessing RAM at 0x0000..0x1fff. This is also the only mode |
48 |
that allows 24-bit addressing, and thus the only mode that allows Mac |
49 |
Classic emulation. The 68k processor is emulated with the UAE CPU engine |
50 |
and two memory areas are allocated for Mac RAM and ROM. The memory map |
51 |
seen by the emulated CPU and the host CPU are different. Mac RAM starts at |
52 |
address 0 for the emulated 68k, but it may start at a different address for |
53 |
the host CPU. |
54 |
|
55 |
In order to handle the particularities of each memory area (RAM, ROM and |
56 |
Frame Buffer), the address space of the emulated 68k is broken down into |
57 |
banks. Each bank is associated with a series of pointers to specific |
58 |
memory access functions that carry out the necessary operations (e.g. |
59 |
byte-swapping, catching illegal writes to memory). A generic memory access |
60 |
function, get_long() for example, goes through the table of memory banks |
61 |
(mem_banks) and fetches the appropriate specific memory access fonction, |
62 |
lget() in our example. This slows down the emulator, of course. |
63 |
|
64 |
2. Emulated CPU, "direct" addressing (EMULATED_68K = 1, DIRECT_ADDRESSING = 1): |
65 |
As in the virtual addressing mode, the 68k processor is emulated with the |
66 |
UAE CPU engine and two memory areas are set up for RAM and ROM. Mac RAM |
67 |
starts at address 0 for the emulated 68k, but it may start at a different |
68 |
address for the host CPU. Besides, the virtual memory areas seen by the |
69 |
emulated 68k are separated by exactly the same amount of bytes as the |
70 |
corresponding memory areas allocated on the host CPU. This means that |
71 |
address translation simply implies the addition of a constant offset |
72 |
(MEMBaseDiff). Therefore, the memory banks are no longer used and the |
73 |
memory access functions are replaced by inline memory accesses. |
74 |
|
75 |
3. Emulated CPU, "real" addressing (EMULATED_68K = 1, REAL_ADDRESSING = 1): |
76 |
This mode is intended for non-68k systems that do allow access to RAM |
77 |
at 0x0000..0x1fff. As in the virtual addressing mode, the 68k processor |
78 |
is emulated with the UAE CPU engine and two areas are allocated for RAM |
79 |
and ROM but the emulated CPU lives in the same address space as the host |
80 |
CPU. This means that if something is located at a certain address for |
81 |
the 68k, it is located at the exact same address for the host CPU. Mac |
82 |
addresses and host addresses are the same. The memory accesses of the CPU |
83 |
emulation still go through access functions but the address translation |
84 |
is no longer needed. The memory access functions are replaced by direct, |
85 |
inlined memory accesses, making for the fastest possible speed of the |
86 |
emulator. On little-endian systems, byte-swapping is still required, of |
87 |
course. |
88 |
|
89 |
A usual consequence of the real addressing mode is that the Mac RAM doesn't |
90 |
any longer begin at address 0 for the Mac and that the Mac ROM also is not |
91 |
located where it usually is on a real Mac. But as the Mac ROM is relocatable |
92 |
and the available RAM is defined for MacOS by the start of the system zone |
93 |
(which is relocated to the start of the allocated RAM area) and the MemTop |
94 |
variable (which is also set correctly) this is not a problem. There is, |
95 |
however, one RAM area that must lie in a certain address range. This area |
96 |
contains the Mac "Low Memory Globals" which (on a Mac II) are located at |
97 |
0x0000..0x1fff and which cannot be moved to a different address range. |
98 |
The Low Memory Globals constitute of many important MacOS and application |
99 |
global variables (e.g. the above mentioned "MemTop" variable which is |
100 |
located at 0x0108). For the real addressing mode to work, the host CPU |
101 |
needs access to 0x0000..0x1fff. Under most operating systems, this is a |
102 |
big problem. On some systems, patches (like PrepareEmul on the Amiga or |
103 |
the sheep_driver under BeOS) can be installed to "open up" this area. On |
104 |
other systems, it might be possible to use access exception handlers to |
105 |
emulate accesses to this area. But if the Low Memory Globals area cannot |
106 |
be made available, using the real addressing mode is not possible. |
107 |
|
108 |
Note: currently, real addressing mode is known to work only on AmigaOS, |
109 |
NetBSD/m68k, FreeBSD/i386 and Linux/i386. |
110 |
|
111 |
4. Native CPU (EMULATED_68K = 0, this also requires REAL_ADDRESSING = 1) |
112 |
This mode is designed for systems that use a 68k (68020 or better) processor |
113 |
as host CPU and is the technically most difficult mode to handle. The Mac |
114 |
CPU is no longer emulated (the UAE CPU emulation is not needed) but MacOS |
115 |
and Mac applications run natively on the existing 68k CPU. This means that |
116 |
the emulator has its maximum possible speed (very close to that of a real |
117 |
Mac with the same CPU). As there is no control over the memory accesses of |
118 |
the CPU, real addressing mode is implied, and so the Low Memory area must |
119 |
be accessible (an MMU might be used to set up different address spaces for |
120 |
the Mac and the host, but this is not implemented in Basilisk II). The |
121 |
native CPU mode has some possible pitfalls that might make its |
122 |
implementation difficult on some systems: |
123 |
a) Implied real addressing (this also means that Mac programs that go out |
124 |
of control can crash the emulator or the whole system) |
125 |
b) MacOS and Mac applications assume that they always run in supervisor |
126 |
mode (more precisely, they assume that they can safely use certain |
127 |
priviledged instructions, mostly for interrupt control). So either |
128 |
the whole emulator has to be run in supervisor mode (which usually is |
129 |
not possible on multitasking systems) or priviledged instructions have |
130 |
to be trapped and emulated. The Amiga and NetBSD/m68k versions of |
131 |
Basilisk II use the latter approach (it is possible to run supervisor |
132 |
mode tasks under the AmigaOS multitasking kernel (ShapeShifter does |
133 |
this) but it requires modifying the Exec task switcher and makes the |
134 |
emulator more unstable). |
135 |
c) On multitasking systems, interrupts can usually not be handled as on |
136 |
a real Mac (or with the UAE CPU). The interrupt levels of the host |
137 |
will not be the same as on a Mac, and the operating systems might not |
138 |
allow installing hardware interrupt handlers or the interrupt handlers |
139 |
might have different stack frames and run-time environments than 68k |
140 |
hardware interrupts. The usual solution is to use some sort of software |
141 |
interrupts or signals to interrupt the main emulation process and to |
142 |
manually call the Mac 68k interrupt handler with a faked stack frame. |
143 |
d) 68060 systems are a small problem because there is no Mac that ever |
144 |
used the 68060 and MacOS doesn't know about this processor. Basilisk II |
145 |
reports the 68060 as being a 68040 to the MacOS and patches some places |
146 |
where MacOS makes use of certain 68040-specific features such as the |
147 |
FPU state frame layout or the PTEST instruction. Also, Basilisk II |
148 |
requires that all of the Motorola support software for the 68060 to |
149 |
emulate missing FPU and integer instructions and addressing modes is |
150 |
provided by the host operating system (this also applies to the 68040). |
151 |
e) The "EMUL_OP" mechanism described below requires the interception and |
152 |
handling of certain emulator-defined instructions. |
153 |
|
154 |
3. Memory access |
155 |
---------------- |
156 |
|
157 |
There is often a need to access Mac RAM and ROM inside the emulator. As |
158 |
Basilisk II may run in "real" or "virtual" addressing mode on many different |
159 |
architectures, big-endian or little-endian, certain platform-independent |
160 |
data types and functions are provided: |
161 |
|
162 |
a) "sysdeps.h" defines the types int8, uint8, int16, uint16, int32 and uint32 |
163 |
for numeric quantities of a certain signedness and bit length |
164 |
b) "cpu_emulation.h" defines the ReadMacInt*() and WriteMacInt*() functions |
165 |
which should always be used to read from or write to Mac RAM or ROM |
166 |
c) "cpu_emulation.h" also defines the Mac2HostAddr() function that translates |
167 |
a Mac memory address to a (uint8 *) in host address space. This allows you |
168 |
to access larger chunks of Mac memory directly, without going through the |
169 |
read/write functions for every access. But doing so you have to perform |
170 |
any needed endianess conversion of the data yourself by using the ntohs() |
171 |
etc. macros which are available on most systems or defined in "sysdeps.h". |
172 |
|
173 |
4. Calling native routines from 68k mode and vice-versa |
174 |
------------------------------------------------------- |
175 |
|
176 |
An emulator like Basilisk II requires two kinds of cross-platform function |
177 |
calls: |
178 |
|
179 |
a) Calling a native routine from the Mac 68k context |
180 |
b) Calling a Mac 68k routine from the native context |
181 |
|
182 |
Situation a) arises in nearly all Basilisk drivers and system patches while |
183 |
case b) is needed for the invocation of Mac call-back or interrupt routines. |
184 |
Basilisk II tries to solve both problems in a way that provides the same |
185 |
interface whether it is running on a 68k or a non-68k system. |
186 |
|
187 |
4.1. The EMUL_OP mechanism |
188 |
-------------------------- |
189 |
|
190 |
Calling native routines from the Mac 68k context requires breaking out of the |
191 |
68k emulator or interrupting the current instruction flow and is done via |
192 |
unimplemented 68k opcodes (called "EMUL_OP" opcodes). Basilisk II uses opcodes |
193 |
of the form 0x71xx (these are invalid MOVEQ opcodes) which are defined in |
194 |
"emul_op.h". When such an opcode is encountered, whether by the emulated CPU |
195 |
or a real 68k, the execution is interrupted, all CPU registers saved and the |
196 |
EmulOp() function from "emul_op.cpp" is called. EmulOp() decides which opcode |
197 |
caused the interrupt and performs the required actions (mostly by calling other |
198 |
emulator routines). The EMUL_OP handler routines have access to nearly all of |
199 |
the 68k user mode registers (exceptions being the PC, A7 and SR). So the |
200 |
EMUL_OP opcodes can be thought of as extensions to the 68k instruction set. |
201 |
Some of these opcodes are used to implement ROM or resource patches because |
202 |
they only occupy 2 bytes and there is sometimes not more room for a patch. |
203 |
|
204 |
4.2. Execute68k() |
205 |
----------------- |
206 |
|
207 |
"cpu_emulation.h" declares the functions Execute68k() and Execute68kTrap() to |
208 |
call Mac 68k routines or MacOS system traps from inside an EMUL_OP handler |
209 |
routine. They allow setting all 68k user mode registers (except PC and SR) |
210 |
before the call and examining all register contents after the call has |
211 |
returned. EMUL_OP and Execute68k() may be nested, i.e. a routine called with |
212 |
Execute68k() may contain EMUL_OP opcodes and the EMUL_OP handlers may in turn |
213 |
call Execute68k() again. |
214 |
|
215 |
5. Interrupts |
216 |
------------- |
217 |
|
218 |
Various parts of Basilisk II (such as the Time Manager and the serial driver) |
219 |
need an interrupt facility to trigger asynchronous events. The MacOS uses |
220 |
different 68k interrupt levels for different events, but for simplicity |
221 |
Basilisk II only uses level 1 and does it's own interrupt dispatching. The |
222 |
"InterruptFlags" contains a bit mask of the pending interrupts. These are the |
223 |
currently defined interrupt sources (see main.h): |
224 |
|
225 |
INTFLAG_60HZ - MacOS 60Hz interrupt (unlike a real Mac, we also handle |
226 |
VBL interrupts and the Time Manager here) |
227 |
INTFLAG_1HZ - MacOS 1Hz interrupt (updates system time) |
228 |
INTFLAG_SERIAL - Interrupt for serial driver I/O completion |
229 |
INTFLAG_ETHER - Interrupt for Ethernet driver I/O completion and packet |
230 |
reception |
231 |
INTFLAG_AUDIO - Interrupt for audio "next block" requests |
232 |
INTFLAG_TIMER - Reserved for a future implementation of a more precise |
233 |
Time Manager (currently not used) |
234 |
INTFLAG_ADB - Interrupt for mouse/keyboard input |
235 |
INTFLAG_NMI - NMI for debugging (not supported on all platforms) |
236 |
|
237 |
An interrupt is triggered by calling SetInterruptFlag() with the desired |
238 |
interrupt flag constant and then TriggerInterrupt(). When the UAE 68k |
239 |
emulator is used, this will signal a hardware interrupt to the emulated 680x0. |
240 |
On a native 68k machine, some other method for interrupting the MacOS thread |
241 |
has to be used (e.g. on AmigaOS, a signal exception is used). Care has to be |
242 |
taken because with the UAE CPU, the interrupt will only occur when Basilisk II |
243 |
is executing MacOS code while on a native 68k machine, the interrupt could |
244 |
occur at any time (e.g. inside an EMUL_OP handler routine). In any case, the |
245 |
MacOS thread will eventually end up in the level 1 interrupt handler which |
246 |
contains an M68K_EMUL_OP_IRQ opcode. The opcode handler in emul_op.cpp will |
247 |
then look at InterruptFlags and decide which routines to call. |
248 |
|
249 |
6. Parts of Basilisk II |
250 |
----------------------- |
251 |
|
252 |
The conception of Basilisk II is quite modular and consists of many parts |
253 |
which are relatively independent from each other: |
254 |
|
255 |
- UAE CPU engine ("uae_cpu/*", not needed on all systems) |
256 |
- ROM patches ("rom_patches.cpp", "slot_rom.cpp" and "emul_op.cpp") |
257 |
- resource patches ("rsrc_patches.cpp" and "emul_op.cpp") |
258 |
- PRAM Utilities replacement ("xpram.cpp") |
259 |
- ADB Manager replacement ("adb.cpp") |
260 |
- Time Manager replacement ("timer.cpp") |
261 |
- SCSI Manager replacement ("scsi.cpp") |
262 |
- video driver ("video.cpp") |
263 |
- audio component ("audio.cpp") |
264 |
- floppy driver ("sony.cpp") |
265 |
- disk driver ("disk.cpp") |
266 |
- CD-ROM driver ("cdrom.cpp") |
267 |
- external file system ("extfs.cpp") |
268 |
- serial drivers ("serial.cpp") |
269 |
- Ethernet driver ("ether.cpp") |
270 |
- system-dependant device access ("sys_*.cpp") |
271 |
- user interface strings ("user_strings.cpp") |
272 |
- preferences management ("prefs.cpp" and "prefs_editor_*.cpp") |
273 |
|
274 |
Most modules consist of a platform-independant part (such as video.cpp) and a |
275 |
platform-dependant part (such as video_beos.cpp). The "dummy" directory |
276 |
contains generic "do-nothing" versions of some of the platform-dependant |
277 |
parts to aid in testing and porting. |
278 |
|
279 |
6.1. UAE CPU engine |
280 |
------------------- |
281 |
|
282 |
All files relating to the UAE 680x0 emulation are kept in the "uae_cpu" |
283 |
directory. The "cpu_emulation.h" header file defines the link between the |
284 |
UAE CPU and the rest of Basilisk II, and "basilisk_glue.cpp" implements the |
285 |
link. It should be possible to replace the UAE CPU with a different 680x0 |
286 |
emulation by creating a new "xxx_cpu" directory with an appropriate |
287 |
"cpu_emulation.h" header file (for the inlined memory access functions) and |
288 |
writing glue code between the functions declared in "cpu_emulation.h" and |
289 |
those provided by the 680x0 emulator. |
290 |
|
291 |
6.2. ROM and resource patches |
292 |
----------------------------- |
293 |
|
294 |
As described above, instead of emulating custom Mac hardware, Basilisk II |
295 |
provides replacements for certain parts of MacOS to redirect input, output |
296 |
and system control functions of the Mac hardware to the underlying operating |
297 |
systems. This is done by applying patches to the Mac ROM ("ROM patches") and |
298 |
the MacOS system file ("resource patches", because nearly all system |
299 |
software is contained in MacOS resources). Unless resources are written back |
300 |
to disk, the system file patches are not permanent (it would cause many |
301 |
problems if they were permanent, because some of the patches vary with |
302 |
different versions of Basilisk II or even every time the emulator is |
303 |
launched). |
304 |
|
305 |
ROM patches are contained in "rom_patches.cpp" and resource patches are |
306 |
contained in "rsrc_patches.cpp". The ROM patches are far more numerous |
307 |
because nearly all the software needed to run MacOS is contained in the Mac |
308 |
ROM (the system file itself consists mainly of ROM patches, in addition to |
309 |
pictures and text). One part of the ROM patches involves the construction of |
310 |
a NuBus slot declaration ROM (in "slot_rom.cpp") which is used to add the |
311 |
video and Ethernet drivers. Apart from the CPU emulation, the ROM and |
312 |
resource patches contain most of the "logic" of the emulator. |
313 |
|
314 |
6.3. PRAM Utilities |
315 |
------------------- |
316 |
|
317 |
MacOS stores certain nonvolatile system parameters in a 256 byte battery |
318 |
backed-up CMOS RAM area called "Parameter RAM", "PRAM" or "XPRAM" (which |
319 |
refers to "Extended PRAM" because the earliest Mac models only had 20 bytes |
320 |
of PRAM). Basilisk II patches the ClkNoMem() MacOS trap which is used to |
321 |
access the XPRAM (apart from some routines which are only used early during |
322 |
system startup) and the real-time clock. The XPRAM is emulated in a 256 byte |
323 |
array which is saved to disk to preserve the contents for the next time |
324 |
Basilisk is launched. |
325 |
|
326 |
6.4. ADB Manager |
327 |
---------------- |
328 |
|
329 |
For emulating a mouse and a keyboard, Basilisk II patches the ADBOp() MacOS |
330 |
trap. Platform-dependant code reports mouse and keyboard events with the |
331 |
ADBMouseDown() etc. functions where they are queued, and the INTFLAG_ADB |
332 |
interrupt is triggered. The ADBInterrupt() handler function sends the input |
333 |
events to MacOS by calling the ADB mouse and keyboard handlers with |
334 |
Execute68k(). |
335 |
|
336 |
6.5. Time Manager |
337 |
----------------- |
338 |
|
339 |
Basilisk II completely replaces the Time Manager (InsTime(), RmvTime(), |
340 |
PrimeTime() and Microseconds() traps). A "TMDesc" structure is associated |
341 |
with each Time Manager task, that contains additional data. The tasks are |
342 |
executed in the TimerInterrupt() function which is currently called inside |
343 |
the 60Hz interrupt handler, thus limiting the resolution of the Time Manager |
344 |
to 16.6ms. |
345 |
|
346 |
6.6. SCSI Manager |
347 |
----------------- |
348 |
|
349 |
The (old-style) SCSI Manager is also completely replaced and the MacOS |
350 |
SCSIDispatch() trap redirected to the routines in "scsi.cpp". Under the |
351 |
MacOS, programs have to issue multiple calls for all the different phases of |
352 |
a SCSI bus interaction (arbitration, selection, command transfer etc.). |
353 |
Basilisk II maps this API to an atomic API which is used by most modern |
354 |
operating systems. All action is deferred until the call to SCSIComplete(). |
355 |
The TIB (Transfer Instruction Block) mini-programs used by the MacOS are |
356 |
translated into a scatter/gather list of data blocks. Operating systems that |
357 |
don't support scatter/gather SCSI I/O will have to use buffering if more |
358 |
than one data block is being transmitted. Some more advanced (but rarely |
359 |
used) aspects of the SCSI Manager (like messaging and compare operations) |
360 |
are not emulated. |
361 |
|
362 |
6.7. Video driver |
363 |
----------------- |
364 |
|
365 |
The NuBus slot declaration ROM constructed in "slot_rom.cpp" contains a |
366 |
driver definition for a video driver. The Control and Status calls of this |
367 |
driver are implemented in "video.cpp". |
368 |
|
369 |
The host-side initialization of the video system is done in VideoInit(). |
370 |
This function must fill the VideoModes vector with a list of supported video |
371 |
modes (combinations of color depth and resolution). It must then call |
372 |
video_init_depth_list() and setup the VideoMonitor structure with the |
373 |
default mode information and the address of a frame buffer for MacOS. In |
374 |
real addressing mode, this frame buffer must be in a MacOS compatible layout |
375 |
(big-endian and 1, 2, 4 or 8 bits paletted chunky pixels, RGB 5:5:5 or xRGB |
376 |
8:8:8:8). In virtual addressing mode, the frame buffer is located at address |
377 |
0xa0000000 on the Mac side and you have to supply the host address, size and |
378 |
layout (BasiliskII will do an automatic pixel format conversion in virtual |
379 |
addressing mode) in the variables MacFrameBaseHost, MacFrameSize and |
380 |
MacFrameLayout. |
381 |
|
382 |
There are two functions of the platform-dependant video driver code that get |
383 |
called during runtime: video_set_palette() to update the CLUT (for indexed |
384 |
modes) or gamma table (for direct color modes), and video_switch_to_mode() |
385 |
to switch to a different color depth and/or resolution (in this case the |
386 |
frame buffer base in VideoMonitor must be updated). |
387 |
|
388 |
6.8. Audio component |
389 |
-------------------- |
390 |
|
391 |
Basilisk II provides a Sound Manager 3.x audio component for sound output. |
392 |
Earlier Sound Manager versions that don't use components but 'snth' |
393 |
resources are not supported. Nearly all component functions are implemented |
394 |
in "audio.cpp". The system-dependant modules ("audio_*.cpp") handle the |
395 |
initialization of the audio hardware/driver, volume controls, and the actual |
396 |
sound output. |
397 |
|
398 |
The mechanism of sound output varies depending on the platform but usually |
399 |
there will be one "streaming thread" (either a thread that continuously |
400 |
writes data buffers to the audio device or a callback function that provides |
401 |
the next data buffer) that reads blocks of sound data from the MacOS Sound |
402 |
Manager and writes them to the audio device. To request the next data |
403 |
buffer, the streaming thread triggers the INTFLAG_AUDIO interrupt which will |
404 |
cause the MacOS thread to eventually call AudioInterrupt(). Inside |
405 |
AudioInterrupt(), the next data block will be read and the streaming thread |
406 |
is signalled that new audio data is available. |
407 |
|
408 |
6.9. Floppy, disk and CD-ROM drivers |
409 |
------------------------------------ |
410 |
|
411 |
Basilisk II contains three MacOS drivers that implement floppy, disk and |
412 |
CD-ROM access ("sony.cpp", "disk.cpp" and "cdrom.cpp"). They rely heavily on |
413 |
the functionality provided by the "sys_*.cpp" module. BTW, the name ".Sony" |
414 |
of the MacOS floppy driver comes from the fact that the 3.5" floppy drive in |
415 |
the first Mac models was custom-built for Apple by Sony (this was one of the |
416 |
first applications of the 3.5" floppy format which was also invented by |
417 |
Sony). |
418 |
|
419 |
6.10. External file system |
420 |
-------------------------- |
421 |
|
422 |
Basilisk II also provides a method for accessing files and direcories on the |
423 |
host OS from the MacOS side by means of an "external" file system |
424 |
(henceforth called "ExtFS"). The ExtFS is built upon the File System Manager |
425 |
1.2 interface that is built into MacOS 7.6 (and later) and available as a |
426 |
system extension for earlier MacOS versions. Unlike other parts of Basilisk |
427 |
II, extfs.cpp requires POSIX file I/O and this is not going to change any |
428 |
time soon, so if you are porting Basilisk II to a system without POSIX file |
429 |
functions, you should emulate them. |
430 |
|
431 |
6.11. Serial drivers |
432 |
-------------------- |
433 |
|
434 |
Similar to the disk drivers, Basilisk II contains replacement serial drivers |
435 |
for the emulation of Mac modem and printer ports. To avoid duplicating code, |
436 |
both ports are handled by the same set of routines. The SerialPrime() etc. |
437 |
functions are mostly wrappers that determine which port is being accessed. |
438 |
All the real work is done by the "SERDPort" class which is subclassed by the |
439 |
platform-dependant code. There are two instances (for port A and B) of the |
440 |
subclasses. |
441 |
|
442 |
Unlike the disk drivers, the serial driver must be able to handle |
443 |
asynchronous operations. Calls to SerialPrime() will usually not actually |
444 |
transmit or receive data but delegate the action to an independant thread. |
445 |
SerialPrime() then returns "1" to indicate that the I/O operation is not yet |
446 |
completed. The completion of the I/O request is signalled by calling the |
447 |
MacOS trap "IODone". However, this can't be done by the I/O thread because |
448 |
it's not in the right run-time environment to call MacOS functions. |
449 |
Therefore it will trigger the INTFLAG_SERIAL interrupt which causes the |
450 |
MacOS thread to eventually call SerialInterrupt(). SerialInterrupt(), in |
451 |
turn, will not call IODone either but install a Deferred Task to do the job. |
452 |
The Deferred Task will be called by MacOS when it returns to interrupt level |
453 |
0. This mechanism sounds complicated but is necessary to ensure stable |
454 |
operation of the serial driver. |
455 |
|
456 |
6.12. Ethernet driver |
457 |
--------------------- |
458 |
|
459 |
A driver for Ethernet networking is also contained in the NuBus slot ROM. |
460 |
Only one ethernet card can be handled by Basilisk II. For Ethernet to work, |
461 |
Basilisk II must be able to send and receive raw Ethernet packets, including |
462 |
the 14-byte header (destination and source address and type/length field), |
463 |
but not including the 4-byte CRC. This may not be possible on all platforms |
464 |
or it may require writing special net drivers or add-ons or running with |
465 |
superuser priviledges to get access to the raw packets. |
466 |
|
467 |
For situations in which access to raw Ethernet packets is not possible, |
468 |
Basilisk II implements a special "tunneling" mode in which it sends and |
469 |
receives packets via UDP, using BSD socket functions. It simply wraps the |
470 |
Ethernet packets into UDP packets, using dummy Ethernet addresses that are |
471 |
made up of the IP address of the host. Ethernet broadcast and AppleTalk |
472 |
multicast packets are sent to the IP broadcast address. Because of this |
473 |
non-standard way of tunneling, it is only possible to set up a "virtual" |
474 |
network amongst machines running Basilisk II in this way. |
475 |
|
476 |
Writing packets works as in the serial drivers. The ether_write() routine |
477 |
may choose to send the packet immediately (e.g. under BeOS) and return noErr |
478 |
or to delegate the sending to a separate thread (e.g. under AmigaOS) and |
479 |
return "1" to indicate that the operation is still in progress. For the |
480 |
latter case, a Deferred Task structure is provided in the ether_data area to |
481 |
call IODone from EtherInterrupt() when the packet write is complete (see |
482 |
above for a description of the mechanism). |
483 |
|
484 |
Packet reception is a different story. First of all, there are two methods |
485 |
provided by the MacOS Ethernet driver API to read packets, one of which |
486 |
(ERead/ ERdCancel) is not supported by Basilisk II. Basilisk II only |
487 |
supports reading packets by attaching protocol handlers. This shouldn't be a |
488 |
problem because the only network code I've seen so far that uses ERead is |
489 |
some Apple sample code. AppleTalk, MacTCP, MacIPX, OpenTransport etc. all |
490 |
use protocol handlers. By attaching a protocol handler, the user of the |
491 |
Ethernet driver supplies a handler routine that should be called by the |
492 |
driver upon reception of Ethernet packets of a certain type. 802.2 packets |
493 |
(type/length field of 0..1500 in the packet header) are a bit special: there |
494 |
can be only one protocol handler attached for 802.2 packets (by specifying a |
495 |
packet type of "0"). The MacOS LAP Manager will attach a 802.2 handler upon |
496 |
startup and handle the distribution of 802.2 packets to sub-protocol |
497 |
handlers, but the Basilisk II Ethernet driver is not concerned with this. |
498 |
|
499 |
When the driver receives a packet, it has to look up the protocol handler |
500 |
installed for the respective packet type (if any has been installed at all) |
501 |
and call the packet handler routine. This must be done with Execute68k() |
502 |
from the MacOS thread, so an interrupt (INTFLAG_ETHER) is triggered upon |
503 |
reception of a packet so the EtherInterrupt() routine can call the protocol |
504 |
handler. Before calling the handler, the Ethernet packet header has to be |
505 |
copied to MacOS RAM (the "ed_RHA" field of the ether_data structure is |
506 |
provided for this). The protocol handler will read the packet data by means |
507 |
of the ReadPacket/ReadRest routines supplied by the Ethernet driver. Both |
508 |
routines will eventually end up in EtherReadPacket() which copies the data |
509 |
to Mac address space. EtherReadPacket() requires the host address and length |
510 |
of the packet to be loaded to a0 and d1 before calling the protocol handler. |
511 |
|
512 |
Does this sound complicated? You are probably right. Here is another |
513 |
description of what happens upon reception of a packet: |
514 |
1. Ethernet card receives packet and notifies some platform-dependant entity |
515 |
inside Basilisk II |
516 |
2. This entity will store the packet in some safe place and trigger the |
517 |
INTFLAG_ETHER interrupt |
518 |
3. The MacOS thread will execute the EtherInterrupt() routine and look for |
519 |
received packets |
520 |
4. If a packet was received of a type to which a protocol handler had been |
521 |
attached, the packet header is copied to ed_RHA, a0/d1 are loaded with |
522 |
the host address and length of the packet data, a3 is loaded with the |
523 |
Mac address of the first byte behing ed_RHA and a4 is loaded with the |
524 |
Mac address of the ed_ReadPacket code inside ether_data, and the protocol |
525 |
handler is called with Execute68k() |
526 |
5. The protocol handler will eventually try to read the packet data with |
527 |
a "jsr (a4)" or "jsr 2(a4)" |
528 |
6. This will execute an M68K_EMUL_OP_ETHER_READ_PACKET opcode |
529 |
7. The EtherReadPacket() opcode handling routine will copy the requested |
530 |
part of the packet data to Mac RAM using the pointer and length which are |
531 |
still in a0/d1 |
532 |
|
533 |
For a more detailed description of the Ethernet driver, see the book "Inside |
534 |
AppleTalk". |
535 |
|
536 |
6.13. System-dependant device access |
537 |
------------------------------------ |
538 |
|
539 |
The method for accessing floppy drives, hard disks, CD-ROM drives and files |
540 |
vary greatly between different operating systems. To make Basilisk II easily |
541 |
portable, all device I/O is made via the functions declared in "sys.h" and |
542 |
implemented by the (system-dependant) "sys_*.cpp" modules which provides a |
543 |
standard, Unix-like interface to all kinds of devices. |
544 |
|
545 |
6.14. User interface strings |
546 |
---------------------------- |
547 |
|
548 |
To aid in localization, all user interface strings of Basilisk II are |
549 |
collected in "user_strings.cpp" (for common strings) and |
550 |
"user_strings_*.cpp" (for platform-specific strings), and accessed via the |
551 |
GetString() function. This way, Basilisk II may be easily translated to |
552 |
different languages. |
553 |
|
554 |
6.15. Preferences management |
555 |
---------------------------- |
556 |
|
557 |
The module "prefs.cpp" handles user preferences in a system-independant way. |
558 |
Preferences items are accessed with the PrefsAdd*(), PrefsReplace*() and |
559 |
PrefsFind*() functions and stored in human-readable and editable text files |
560 |
on disk. There are two lists of available preferences items. The first one, |
561 |
common_prefs_items, is defined in "prefs_items.cpp" and lists items which |
562 |
are available on all systems. The second one, platform_prefs_items, is |
563 |
defined in "prefs_*.cpp" and lists the prefs items which are specific to a |
564 |
certain platform. |
565 |
|
566 |
The "prefs_editor_*.cpp" module provides a graphical user interface for |
567 |
setting the preferences so users won't have to edit the preferences file |
568 |
manually. |
569 |
|
570 |
7. Porting Basilisk II |
571 |
---------------------- |
572 |
|
573 |
Porting Basilisk II to a new platform should not be hard. These are the |
574 |
steps involved in the process: |
575 |
|
576 |
1. Create a new directory inside the "src" directory for your platform. If |
577 |
your platform comes in several "flavours" that require adapted files, you |
578 |
should consider creating subdirectories inside the platform directory. |
579 |
All files needed for your port must be placed inside the new directory. |
580 |
Don't scatter platform-dependant files across the "src" hierarchy. |
581 |
2. Decide in which mode (virtual addressing, real addressing or native CPU) |
582 |
Basilisk II will run. |
583 |
3. Create a "sysdeps.h" file which defines the mode and system-dependant |
584 |
data types and memory access functions. Things which are used in Basilisk |
585 |
but missing on your platform (such as endianess macros) should also be |
586 |
defined here. |
587 |
4. Implement the system-specific parts of Basilisk: |
588 |
main_*.cpp, sys_*.cpp, prefs_*.cpp, prefs_editor_*.cpp, xpram_*.cpp, |
589 |
timer_*.cpp, audio_*.cpp, video_*.cpp, serial_*.cpp, ether_*.cpp, |
590 |
scsi_*.cpp and clip_*.cpp |
591 |
You may want to take the skeleton implementations in the "dummy" directory |
592 |
as a starting point and look at the implementation for other platforms |
593 |
before writing your own. |
594 |
5. Important things to remember: |
595 |
- Use the ReadMacInt*() and WriteMacInt*() functions from "cpu_emulation.h" |
596 |
to access Mac memory |
597 |
- Use the ntohs() etc. macros to convert endianess when accessing Mac |
598 |
memory directly |
599 |
- Don't modify any source files outside of your platform directory unless |
600 |
you really, really have to. Instead of adding "#ifdef PLATFORM" blocks |
601 |
to one of the platform-independant source files, you should contact me |
602 |
so that we may find a more elegant and more portable solution. |
603 |
6. Coding style: indent -kr -ts4 |
604 |
|
605 |
|
606 |
Christian Bauer |
607 |
<Christian.Bauer@uni-mainz.de> |