642
|
1 <?xml version="1.0" ?>
|
|
2 <!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
|
3 "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
|
4 <!--
|
|
5 The author has not been contacted about adding this article to the
|
|
6 documentation set.
|
|
7 Original URL: http://www.onastick.clara.net/os9sw.htm
|
469
|
8 -->
|
|
9 <article>
|
|
10 <articleinfo>
|
642
|
11 <author><firstname>Jon</firstname><surname>Bird</surname></author>
|
469
|
12 <title>Software for OS9</title>
|
|
13 </articleinfo>
|
|
14 <para>
|
|
15 There has been a fair deal of material written on using OS9, applications and
|
|
16 filters but very little that I have seen (in the Dragon world anyway) about
|
|
17 actually writing software for OS9. I think most people only tend to use it for
|
|
18 Stylograph and such like, because it is a completly different environent to
|
|
19 the Dragon in it's native mode and can to be honest pretty horrendous to get
|
|
20 to grips with on a normal 64K Dragon. Originally I intended to cover this as
|
|
21 part of the stuff I've been writing on connecting PC's/Dragons but it's such a
|
|
22 big subject I thought breaking it out separatly would be better. Therefore,
|
|
23 I'll also cover device drivers and stuff like that in support of the PC thing,
|
|
24 because OS9 gets a lot better and easier to use by the addition of say an 80
|
|
25 column screen and hard disk.
|
|
26 </para>
|
|
27 <section>
|
|
28 <title>DragonDOS vs OS9</title>
|
|
29 <para>
|
|
30 Trying to explain the difference between a Dragon running normal BASIC/DOS and
|
|
31 one running OS9 is difficult if you have not used one or the other. However,
|
|
32 for the programmer the Dragon is ideal, because you get the best of both
|
|
33 worlds, in 'native' mode where you can program and do exactly what you want
|
|
34 with the machine and under OS9 where things are a lot more tightly managed by
|
|
35 the operating system which is a lot more how it is becoming with Windows and
|
|
36 such like.
|
|
37 </para>
|
|
38 <para>
|
|
39 Essentially, OS9 is composed of a set of 'building blocks' and can easily be
|
|
40 expanded by adding or changing another building block or module. A completly
|
|
41 minmimal setup would therefore consist of the OS9 kernel (called OS9p1 & OS9p2
|
|
42 on a Dragon) which is just a block of core procedures required to manage the
|
|
43 system. It has no idea of keyboards, disks or screens or any input/output come
|
|
44 to that and does not even come with any sort of command interpretor (the shell
|
|
45 utility performs this which is a seperate entity in its own right). In fact on
|
|
46 its own it is pretty much useless on an ordinary home computer. However, OS9
|
|
47 has found its home (as many of the books on it will tell you) in lots of real
|
|
48 time applications, where there is no need for direct human interaction.
|
|
49 However, for most of use some sort of interaction is reqired so you will find
|
|
50 a dedicated module for handling all I/O (called the IO Manager (or IOMAN for
|
|
51 short)) and further modules for dealing with specific types of IO. As an
|
|
52 example, standard OS9 is shipped with two file managers, the Sequential File
|
|
53 Manager (SCFMan) which manages all sequential types of devices, such as the
|
|
54 keyboard, screen and knows all about sequential type peripherals, such as
|
|
55 delete keys, carriage return and other line editing functions. The other file
|
|
56 manager is the Random Block File Manager (RBFMan) which looks after random
|
|
57 block type devices, such as disks. This knows all about what an OS9 disks
|
|
58 looks like, what makes up a file and how to find it. Finally, there are the
|
|
59 drivers, which actually talk to the hardware, and on the Dragon are
|
|
60 specifically designed for the Dragon's hardware. For example, DDISK the Dragon
|
|
61 device driver, knows how to talk to the disk controller chip to read a bit of
|
|
62 the disk. On the other hand, it does not know what a file is. Right at the
|
|
63 bottom of the change are the device descriptors, which are basicly blocks of
|
|
64 memory to tell the system, this is disk drive 0, it uses DDISK to talk to the
|
|
65 disk controller, and RBFMan is it's file manager. A common data flow would
|
|
66 therefore look something like:
|
|
67 <screen>
|
|
68 Your OS9 Program says:
|
|
69 'read 100 bytes from path number n'
|
|
70
|
|
71 Which is passed to IOMAN as:
|
|
72 'read 100 bytes from path number n'
|
|
73 which determines that RBFMan is responsible for path n:
|
|
74
|
|
75 Which is passed to RBFMan as:
|
|
76 'read 100 bytes from path number n'
|
|
77 who knows all about what path n is, and where to get those
|
|
78 100 bytes from:
|
|
79
|
|
80 Which is then passed to DDISk as:
|
|
81 'read sector m'
|
|
82 which actually gets the disk controller to read the data
|
|
83 </screen>
|
|
84 </para>
|
|
85 <para>
|
|
86 This collection of modules forms, in equivalent Dragon terms the BASIC & DOS
|
|
87 ROM starting at 32768 ($8000) onwards. On its own, they provide little more
|
|
88 than procedures and subroutines for using your machine.
|
|
89 </para>
|
|
90 <para>
|
|
91 In order to utilise these routines, and actually get your machine doing some
|
|
92 useful work, some sort of user friendly interface is needed to talk to the
|
|
93 user, under OS9 a program called Shell is provided to do this, and the BASIC
|
|
94 interpretor provides this facility under normal DragonDOS mode. They both work
|
|
95 in a similar manner: each has a set of internal commands which it understands
|
|
96 and can execute, and each has the ability to run external programs. For
|
|
97 example, under Dragon BASIC the line:
|
|
98 <screen>
|
|
99 PRINT "HELLO WORLD"
|
|
100 </screen>
|
|
101 </para>
|
|
102 <para>
|
|
103 is effectivly running at internal program within the interpretor called PRINT.
|
|
104 Likewise with the OS9 Shell:
|
|
105 <screen>
|
|
106 chd /d0
|
|
107 </screen>
|
|
108 </para>
|
|
109 <para>
|
|
110 is understood by the Shell to mean 'change directory to drive D0' and runs the
|
|
111 internal code required to perform this operation.
|
|
112 </para>
|
|
113 <para>
|
|
114 In order to run a program external to the BASIC interpretor, you need to tell
|
|
115 it explicitly, as in:
|
|
116 <screen>
|
|
117 RUN "MYPROG"
|
|
118 </screen>
|
|
119 </para>
|
|
120 <para>
|
|
121 Tells it that MYPROG.BAS is not part of the on board ROM, and that it has to
|
|
122 go away and fetch it from somewhere else (in this case the disk). The Shell
|
|
123 works slightly differently, in that any command you type which is not
|
|
124 recognized by the Shell itself means that is will go away and attempt to find
|
|
125 it externally. For example:
|
|
126 <screen>
|
|
127 dir /d1
|
|
128 </screen>
|
|
129 </para>
|
|
130 <para>
|
|
131 The DIR command is not recognized by the Shell, therefore it automatically
|
|
132 attempts to load and run it from elsewhere. The Shell itself contains
|
|
133 relativly few in-built commands (unlike the BASIC interpretor) and so for the
|
|
134 majority of things will be forcing the execution of an external program.
|
|
135 </para>
|
|
136 <para>
|
|
137 One of the utility programs shipped with Dragon OS9 is MDIR which simply
|
|
138 provides a list of things in memory, or a very simplified OS9 memory map. When
|
|
139 I run it on my system I get something like the following:
|
|
140 <screen>
|
|
141 Module Directory at 10:47:24
|
|
142 ADDR SIZE TY RV AT UC NAME
|
|
143 ---- ---- -- -- -- -- ---------
|
|
144 F05E 7E7 C1 1 r OS9
|
|
145 F853 4FC C1 1 r 1 OS9p2
|
|
146 FD4F 2E C0 1 r 1 Init
|
|
147 FD7D 182 C1 1 r 1 BooT
|
|
148 BF00 122 C1 4 r 1 SYSGO
|
|
149 C022 101 C1 1 r 1 Clock
|
|
150 C123 6E7 C1 1 r 1 IOMan
|
|
151 C80A CF5 D1 1 r 1 Rbf
|
|
152 D4FF 40E D1 1 r 2 Scf
|
|
153 D90D 1FF D1 1 r PipeMan
|
|
154 DB0C 5E2 E1 7 r Ddisk
|
|
155 E0EE 3AF E1 6 r 2 Kbvdio
|
|
156 E49D 8D E1 1 r Printer
|
|
157 E52A 28 E1 1 r Piper
|
|
158 E552 2E F1 1 r D0
|
|
159 E580 2E F1 2 r D1
|
|
160 E5AE 3C F1 1 r 2 Term
|
|
161 E5EA 3A F1 1 r P
|
|
162 E624 26 F1 1 r Pipe
|
|
163 E64A 4FA 11 1 r 2 Shell
|
|
164 EB44 121 E1 3 r 1 Rdisk
|
|
165 EC65 2E F1 1 r 1 R0
|
|
166 EC93 160 E1 1 r PC_Disk
|
|
167 EDF3 30 F1 1 r H0
|
|
168 5A00 1A6 11 1 r 1 Mdir
|
|
169 </screen>
|
|
170 </para>
|
|
171 <para>
|
|
172 The majority of these modules are the drivers, managers and general routines
|
|
173 that make up the 'core' of the OS9 system. In fact, the only programs as such
|
|
174 are the Shell, and the MDIR program itself. One of the things to note, is that
|
|
175 if you run MDIR on your OS9 system, it would almost certainly look different:
|
|
176 not only would you probably have different drivers, they would almost
|
|
177 certainly appear in different areas of memory. This is one of the key
|
|
178 differences between OS9 and normal Dragon BASIC mode, that any program written
|
|
179 for OS9 does not know where it will be in memory and therefore has to be
|
|
180 written to be run from any location in memory. One of the side effects of this
|
|
181 is that code modules in OS9 will probably tend to execute slower that a
|
|
182 DragonDOS counterpart. Part of this is also due to the fact that within the
|
|
183 Dragon BASIC/DOS ROM routines always lie at fixed addresses, and can be called
|
|
184 quickly. OS9 programs have to perform more complex indexing operations to find
|
|
185 routines. Therefore, OS9 always seems considerably more slower and therefore
|
|
186 more painful to work with than DragonDOS.
|
|
187 </para>
|
|
188 <para>
|
|
189 To see this, and some of the pros and cons of writing programs for each
|
|
190 system, here is a couple of 'hello world' routines for each system. Firstly,
|
|
191 the Dragon assembler version - this assumes DASM to be the assembler:
|
|
192 <screen>
|
|
193 10 EXEC &H6C00
|
|
194 20 ALL
|
|
195 30 @MSG FCC 0,"HELLO WORLD",0
|
|
196 40 @START LDX #@MSG
|
|
197 50 JSR $90E5
|
|
198 60 RTS
|
|
199 70 END @START
|
|
200 80 EXEC
|
|
201 </screen>
|
|
202 </para>
|
|
203 <para>
|
|
204 It is a fairly simple piece of code, and also fairly easy to write, assemble
|
|
205 and get running. Firstly, DASM is loaded from disk, you write the code much
|
|
206 like a BASIC program using all the normal BASIC editor functions then just run
|
|
207 it. The program itself makes use of a standard ROM routine, which prints a
|
|
208 string pointed to by the X register. The 'END' statement at line 70 puts the
|
|
209 starting address into the default EXEC location, which is then called by line
|
|
210 80.
|
|
211 </para>
|
|
212 <para>
|
|
213 Onto the OS9 version then:
|
|
214 <screen>
|
|
215 nam WORLD
|
|
216 ttl Hello World program
|
|
217
|
|
218 ifp1
|
|
219 use /d0/defs/os9defs
|
|
220 endc
|
|
221
|
|
222 *Data area
|
|
223 org 0
|
|
224 stack rmb 200
|
|
225 datsiz equ .
|
|
226
|
|
227 *Module header
|
|
228 type equ PRGRM+OBJCT
|
|
229 revs equ REENT+1
|
|
230 mod length,name,type,revs,start,datsiz
|
|
231 name fcs /WORLD/
|
|
232 msg fcc /Hello World/
|
|
233 fcb 13
|
|
234
|
|
235 *main code
|
|
236 start leax msg,pcr
|
|
237 ldy #12
|
|
238 lda #1
|
|
239 os9 i$writln
|
|
240 bcs error
|
|
241 clrb
|
|
242 error os9 f$exit
|
|
243
|
|
244 emod
|
|
245
|
|
246 length equ *
|
|
247 </screen>
|
|
248 </para>
|
|
249 <para>
|
|
250 One of the first problems is actually getting an environment to write this in.
|
|
251 Dragon OS9 comes with an edit command, which in all honestly is complete
|
|
252 rubbish and I'd suggest you use Stylo or another editor. I use a program
|
|
253 called 'ed' which is based on the Unix 'vi' editor. Once you've written it,
|
|
254 saved it to disk, you can assemble it using the OS9 assembler:
|
|
255 <screen>
|
|
256 asm world #20k
|
|
257 </screen>
|
|
258 </para>
|
|
259 <para>
|
|
260 Assumes you've saved the file as world. Assuming all goes well, you will end
|
|
261 up with a file called 'world' in your default execution directory (typically
|
|
262 /d0/cmds). However, it is the norm for OS9 (I have found anyway) that things
|
|
263 are not often that straightforward. Firstly, you may find you don't actually
|
|
264 have a copy of the 'asm' program, and even if you do the program you have just
|
|
265 written requires an additional file (called up by the 'use /d0/defs/os9defs'
|
|
266 statement) which may or may not be on your disk. Either way, you will probably
|
|
267 end up searching through a variety of disks in order to get the files required
|
|
268 to the right place. Once you have, and a great deal of disk chugging later
|
|
269 with any luck, simply typing 'world' at the OS9 shell prompt, should once
|
|
270 again after a bit of disk chugging display the message 'hello world' on your
|
|
271 screen.
|
|
272 </para>
|
|
273 <para>
|
|
274 So how does it all work, and is it all worth it. Firstly, the NAM and TTL
|
|
275 directives just tell the assembler the name and title of your program and are
|
|
276 not strictly required. Next, there is an assembler directive to include the
|
|
277 file /d0/defs/os9defs but only on pass one (the IFP1 condition) of the
|
|
278 assembly. This file contains a list of definitions about OS9 and is almost
|
|
279 always required for any OS9 module. Whilst you can get away without it, for
|
|
280 general neatness and code readability it's a good idea to use it. For example,
|
|
281 in our simple program, the words PRGRM, OBJCT, i$write and f$exit are all
|
|
282 definitions from os9defs.
|
|
283 </para>
|
|
284 <para>
|
|
285 Next the program's data area is defined, which as a minimum should define at
|
|
286 least 200 bytes for the stack (I'll cover data areas later). Following this,
|
|
287 information for the programs module header is defined. In OS9, each program
|
|
288 unit or module must conform to a specific format in order for OS9 to recognize
|
|
289 it and successfully execute it. In particular, it contains information about
|
|
290 the execution address, how much data size is required and also a checksum of
|
|
291 the whole module. All this minimizes the risk of trying to run corrupted or
|
|
292 invalid code. The 'mod' assembler directive tells the assembler to form this
|
|
293 header using the information provided. This requires the overall length of the
|
|
294 module then a pointer to the name of the module. Next are two equates defined
|
|
295 as TYPE and REVS. The TYPE equate is a single byte defining the module type
|
|
296 and language and is preset to a normal program module (PRGRM) and 6809-object
|
|
297 code (OBJCT). The REVS equate is a single byte defining the module attributes
|
|
298 and revision level. It has an attribute of sharable (REENT) and a revision
|
|
299 level of 1. The final two parameters are the start address and overall module
|
|
300 data size.
|
|
301 </para>
|
|
302 <para>
|
|
303 Following the module header is the module name. The FCS directive is much like
|
|
304 the FCC directive except the top bit of the last character is the string is
|
|
305 set. This is often used by OS9 to indicate the end of a string. Finally, the
|
|
306 main code for the program occurs.
|
|
307 </para>
|
|
308 <para>
|
|
309 The code make use of the OS9 system call i$writln. In order to make a system
|
|
310 call, the assembler directive 'os9' is used followed by the call name. All the
|
|
311 valid OS9 call names are defined in the os9defs file. OS9 system calls are
|
|
312 made through one of the software interrupts, in this case SWI2 followed by a
|
|
313 unique code identifying the call number. Therefore, the assembler directive
|
|
314 'os9 i$writln' translates into 'SWI2 $8C' where $8C is the i$writln code.
|
|
315 </para>
|
|
316 <para>
|
|
317 This call is much like the ROM call at $90E5, where the X register points to
|
|
318 the string to display. In addition, however the Y register should also contain
|
|
319 the maximum number of characters to display and the A register should contain
|
|
320 the path number. A path number of 1 refers to the standard output - the
|
|
321 display. The call will then display the required number of characters, or up
|
|
322 to the first carriage return - whichever occurs sooner. On completion of the
|
|
323 call, the carry bit is set if an error has occured with the error number in
|
|
324 the B register. Finally, the routine is terminated with a call to the F$EXIT
|
|
325 routine not an RTS instruction.
|
|
326 </para>
|
|
327 <para>
|
|
328 Once the code is complete, the EMOD statement indicates that this is the end
|
|
329 of the module, and generates the module's checksum and then the length label
|
|
330 is calculated.
|
|
331 </para>
|
|
332 <para>
|
|
333 This program also gives an example writing position independent code ie. code
|
|
334 that can be executed anywhere in memory. Note how the X register in the os9
|
|
335 example is set to point to the string compared to the DragonDOS one:
|
|
336 <screen>
|
|
337 DDOS: LDX #@MSG - absolute address of MSG
|
|
338 OS9: leax msg,pcr - relative address of MSG
|
|
339 </screen>
|
|
340 </para>
|
|
341 <para>
|
|
342 The OS9 variant sets X to point to the MSG label relative to the current
|
|
343 program counter ie. whereever you are in memory. The DragonDOS one specifies
|
|
344 the absolute address in memory. This means that with the DragonDOS code you
|
|
345 are forced to have your program at this location in order for it to work. Of
|
|
346 course, you could replace your DragonDOS one with the same instruction as the
|
|
347 OS9 one and make it position independent, but with OS9 you must use this
|
|
348 format or it will not work.
|
|
349 </para>
|
|
350 </section>
|
|
351 <section>
|
|
352 <title>OS9 Advantages</title>
|
|
353 <para>
|
|
354 From the last couple of articles, we have created two comparable programs
|
|
355 under Dragon BASIC and OS9 - the now famous 'Hello World' program. However,
|
|
356 the steps to create the OS9 one are significantly more complex than the
|
|
357 DragonDOS counterpart. We have already seen one of the OS9 program's
|
|
358 advantages - that of position independent code, though with a bit of
|
|
359 forethought the Dragon BASIC one can also be made position independent. Here
|
|
360 are a few other advantages of the OS9 variant.
|
|
361 </para>
|
|
362 <para>
|
|
363 Suppose you wanted to print the words 'Hello World' on the printer. This can
|
|
364 be accomplished fairly simply under both OS9 and Dragon BASIC:
|
|
365 <screen>
|
|
366 Dragon BASIC: POKE 111,254:EXEC
|
|
367 OS9: world >/p
|
|
368 </screen>
|
|
369 </para>
|
|
370 <para>
|
|
371 The Dragon BASIC variant simply sets the default IO location to 254 (-2) the
|
|
372 printer. The OS9 one utilises the shell re-direction operator (>) to send the
|
|
373 output to the printer. So far so good. What if you wanted to send this message
|
|
374 to a file for example. With standard DragonDOS, there is no easy way - later
|
|
375 DOS variants can use the OPEN# format:
|
|
376 <screen>
|
|
377 OPEN #1,"O","TEST.DAT"
|
|
378 POKE 111,1:EXEC
|
|
379 CLOSE #1
|
|
380 </screen>
|
|
381 </para>
|
|
382 <para>
|
|
383 Once again the OS9 variant is pretty much straightforward:
|
|
384 <screen>
|
|
385 world >test.dat
|
|
386 </screen>
|
|
387 </para>
|
|
388 <para>
|
|
389 Suppose then you have added a custom device to your system, an extra parallel
|
|
390 port for example for a second printer. Under OS9 all you need to do is create
|
|
391 a new device driver/descriptor and call it say P1. The world program can
|
|
392 automatically be used with your new device:
|
|
393 <screen>
|
|
394 world >/p1
|
|
395 </screen>
|
|
396 </para>
|
|
397 <para>
|
|
398 The OS9 world program can then theorhetically be run on any OS9 system and
|
|
399 make use of any device which is implemented on that machine.
|
|
400 </para>
|
|
401 <para>
|
|
402 The other 'selling point' of OS9 is it's multi-tasking ability - ie. run more
|
|
403 than one program at once. This is all handled by the OS9 kernel routines, and
|
|
404 you do not need to worry about it when writing code, except to ensure your
|
|
405 programs are position independent, and re-entrant. A re-entrant program is all
|
|
406 to do with how any data your program utilises is managed and means that one
|
|
407 copy of a program in memory can be running a number of times with a different
|
|
408 set of data.
|
|
409 </para>
|
|
410 </section>
|
|
411 <section>
|
|
412 <title>OS9 Data Storage</title>
|
|
413 <para>
|
|
414 When you write your OS9 program, you should allocate all your data areas using
|
|
415 rmb directives starting at 0 eg.
|
|
416 <screen>
|
|
417 *Data area
|
|
418 org 0 specify starting at 0
|
|
419 length rmb 2 data items for
|
|
420 name rmb 20 the program
|
|
421 next rmb 2
|
|
422 stack rmb 200
|
|
423 datsiz equ .
|
|
424 </screen>
|
|
425 </para>
|
|
426 <para>
|
|
427 Your program's stack is also part of this data area, so you should allocate a
|
|
428 sufficient area for this - 200 bytes is a recommended minimum. The datsiz
|
|
429 label gives the total data size required for the program, and is used as part
|
|
430 of the module header created by the MOD assembler directive:
|
|
431 <screen>
|
|
432 mod length,name,type,revs,start,datsiz
|
|
433 </screen>
|
|
434 </para>
|
|
435 <para>
|
|
436 When OS9 starts your program, it sets up some of the CPU registers to provide
|
|
437 information about your data area-
|
|
438 <screen>
|
|
439 | |
|
|
440 | Parameter Area |
|
|
441 ------------------- -- X, S
|
|
442 | |
|
|
443 | |
|
|
444 | Data Area |
|
|
445 | |
|
|
446 -------------------
|
|
447 | Direct Page |
|
|
448 ------------------- -- U, DP (LS address)
|
|
449 </screen>
|
|
450 </para>
|
|
451 <para>
|
|
452 Firstly, the U and DP registers point to the start of your data area, with the
|
|
453 stack pointer set to the top of your data area. Essentially then, you just
|
|
454 need to reference the U register when accessing your data variables within
|
|
455 your program:
|
|
456 <screen>
|
|
457 sty length,u stores Y into 'length'
|
|
458 ldx next,u loads X from 'next'
|
|
459 </screen>
|
|
460 </para>
|
|
461 <para>
|
|
462 However, any data items stored within the first 256 bytes of your data area
|
|
463 can be accessed more quickly and efficiently by making use of the DP register.
|
|
464 This is an 8 bit register which contains the upper 8 bits of an address. In
|
|
465 order to specify DP addressing with the OS9 assembler simply supply the data
|
|
466 label:
|
|
467 <screen>
|
|
468 sty length use DP addressing to
|
|
469 ldx next access data items
|
|
470 </screen>
|
|
471 </para>
|
|
472 <para>
|
|
473 Using the data declarations used earlier:
|
|
474 <screen>
|
|
475 length lies at offset 0 ($00).
|
|
476 next lies at offset 22 ($16).
|
|
477 </screen>
|
|
478 </para>
|
|
479 <para>
|
|
480 The assembler translates this into:
|
|
481 <screen>
|
|
482 STY $00
|
|
483 LDX $16
|
|
484 </screen>
|
|
485 </para>
|
|
486 <para>
|
|
487 instructing the processor to use the DP register to provide the upper part of
|
|
488 the address. Therefore, if DP=$20 this would become:
|
|
489 <screen>
|
|
490 STY $2000
|
|
491 LDX $2016
|
|
492 </screen>
|
|
493 </para>
|
|
494 <para>
|
|
495 Not only does this form of addressing take up less storage space, it is also
|
|
496 quicker to execute.
|
|
497 </para>
|
|
498 <para>
|
|
499 You will still need to use the U register to set up pointers to data buffers
|
|
500 since you cannot use the DP register for this:
|
|
501 <screen>
|
|
502 leax name,u X=start of name item
|
|
503 </screen>
|
|
504 </para>
|
|
505 <para>
|
|
506 There is an important difference between the way the OS9 assembler and
|
|
507 DragonDOS assemblers (DASM & DREAM) use the DP register. By default, the OS9
|
|
508 assembler will use the DP register if you just enter something like:
|
|
509 <screen>
|
|
510 ldx next -> LDX $16
|
|
511 </screen>
|
|
512 </para>
|
|
513 <para>
|
|
514 However, DragonDOS assemblers will automatically use extended addressing so
|
|
515 assuming @NEXT lies at address $5020.
|
|
516 <screen>
|
|
517 LDX #@NEXT -> LDX $5020
|
|
518 </screen>
|
|
519 </para>
|
|
520 <para>
|
|
521 The assembler directive '>' is used to set extended addressing under OS9 ie.
|
|
522 <screen>
|
|
523 ldx >$16 -> LDX $0016
|
|
524 </screen>
|
|
525 </para>
|
|
526 <para>
|
|
527 and co-incidently is used to force DP addressing under DragonDOS ie.
|
|
528 <screen>
|
|
529 ldx >$F0 -> LDX $F0
|
|
530 </screen>
|
|
531 </para>
|
|
532 <para>
|
|
533 The DP register is nearly always set to 0 under DragonDOS so you can use it to
|
|
534 quickly access memory locations 0-255.
|
|
535 </para>
|
|
536 <para>
|
|
537 So, just by the kernel setting the U and DP registers to different positions
|
|
538 in memory the same piece of code need only be resident in memory once but can
|
|
539 be running in different tasks with different data areas. The REENT bit set in
|
|
540 the module header is used to tell OS9 that our program supports this - if you
|
|
541 recall:
|
|
542 <screen>
|
|
543 *Module header
|
|
544 type equ PRGRM+OBJCT
|
|
545 revs equ REENT+1
|
|
546 mod length,name,type,revs,start,datsiz
|
|
547 name fcs /WORLD/
|
|
548 msg fcc /Hello World/
|
|
549 fcb 13
|
|
550 </screen>
|
|
551 </para>
|
|
552 <para>
|
|
553 The parameter area (pointed to by the X register on program entry) is utilised
|
|
554 to pass data to a program. If it has been started from the shell (command
|
|
555 line) any text following the program name will be stored here terminated by a
|
|
556 carriage return ie.
|
|
557 <screen>
|
|
558 OS9:world this is a parameter
|
|
559 </screen>
|
|
560 </para>
|
|
561 <para>
|
|
562 then X would point to a block of memory containing the ASCII string:
|
|
563 <screen>
|
|
564 this is a parameter[CR]
|
|
565 </screen>
|
|
566 </para>
|
|
567 </section>
|
|
568 <section>
|
|
569 <title>Other Progamming Tips</title>
|
|
570 <para>
|
|
571 Generally, thats about all there is to writing OS9 programs. Remember, that an
|
|
572 OS9 process should exit by calling os9 f$exit (with the B register containing
|
|
573 any error code you wish to return, or 0 for none) instead of rts. The OS9
|
|
574 System Programmers Manual which you should have with your copy of OS9 contains
|
|
575 all the system calls available. However, its worth mentioning a bit about
|
|
576 standard IO paths.
|
|
577 </para>
|
|
578 <para>
|
|
579 We have already seen standard output used with the Hello World program:
|
|
580 <screen>
|
|
581 ldy #12
|
|
582 lda #1 standard output
|
|
583 os9 i$writln
|
|
584 bcs error
|
|
585 </screen>
|
|
586 </para>
|
|
587 <para>
|
|
588 On entry to any OS9 program 3 file paths are open:
|
|
589 <screen>
|
|
590 0 = Standard Input (keyboard)
|
|
591 1 = Standard Output (screen)
|
|
592 2 = Standard Error (screen)
|
|
593 </screen>
|
|
594 </para>
|
|
595 <para>
|
|
596 These may or may not correspond to screen or keyboard depending on whether the
|
|
597 shell or other program has re-directed them ie.
|
|
598 <screen>
|
|
599 world >/p
|
|
600 </screen>
|
|
601 </para>
|
|
602 <para>
|
|
603 forces standard output to be the printer. This means that for the duration of
|
|
604 your program, path number 1 is still valid but points to the printer instead.
|
|
605 This is the premise of a lot of OS9 utilities called filters which typically
|
|
606 perform some kind of modification to incoming data and write the modified data
|
|
607 out. An example might be a program to add line feeds to a file in preparation
|
|
608 to porting it to another environment which requires CR/LF terminated strings
|
|
609 as opposed to CR which OS9 uses. The basis of the filter might be:
|
|
610 <screen>
|
|
611 loop
|
|
612 leax buffer,u point to buffer
|
|
613 ldy #1 1 char to read
|
|
614 clra from stdin
|
|
615 os9 i$read read byte
|
|
616 bcs error handle error
|
|
617 inca set to stdout
|
|
618 os9 i$write write byte
|
|
619 bcs error
|
|
620 ldb ,x was it a CR
|
|
621 cmpb #13
|
|
622 bne loop no, loop for next
|
|
623 ldb #10 write an LF
|
|
624 stb ,x to stdout
|
|
625 os9 i$write
|
|
626 bcs error
|
|
627 bra loop
|
|
628
|
|
629 error cmpb #E$EOF check for EOF
|
|
630 bne term if not, exit with error
|
|
631 clrb else quit ok
|
|
632 term os9 f$exit
|
|
633 </screen>
|
|
634 </para>
|
|
635 <para>
|
|
636 Here we make use of the i$read, i$write calls which read and write 'raw' data.
|
|
637 A byte is read from stdin and then written out again to stdout. If it was a CR
|
|
638 then an LF is also written to stdout. The program will terminate when an error
|
|
639 condition occurs. If all the data is exhausted, this error will be EOF on the
|
|
640 read which is therefore not treated as an error and causes the program to exit
|
|
641 without error. This is the case for all filters. Assuming the program is
|
|
642 assembled to the file 'addlf' an example usage could be with the stylograph
|
|
643 mail merge utility (mm):
|
|
644 <screen>
|
|
645 mm my_doc ! addlf >/d1/my_doc.out
|
|
646 </screen>
|
|
647 </para>
|
|
648 <para>
|
|
649 Here the Stylograph file 'my_doc' is output by mailmerge and piped into our
|
|
650 addlf utility, so stdin is redirected here from the pipe. The resulting output
|
|
651 (stdout) is then sent to the file 'my_doc.out'.
|
|
652 </para>
|
|
653 <para>
|
|
654 This filter isn't very efficient, because it carries the overhead of calling
|
|
655 i$read/i$write for every byte. If you recall, an OS9 system call translates
|
|
656 into an SWI2 (software interrupt) call. This results in a lot of processing,
|
|
657 and also the stacking/de-stacking of all the registers required for an SWI
|
|
658 call. It would be far better to read the data in larger chunks and process it
|
|
659 in a separate loop.
|
|
660 </para>
|
|
661 <para>
|
|
662 Note that any registers not updated explicity for output of a system call will
|
|
663 remain unchanged from when the call was made eg.
|
|
664 <screen>
|
|
665 i$read: Entry: X = Addr of data
|
|
666 Y = Bytes to read
|
|
667 A = Path number
|
|
668 Exit: Y = Bytes actually read
|
|
669 ALL OTHER REGISTERS UNCHANGED
|
|
670 </screen>
|
|
671 </para>
|
|
672 </section>
|
|
673 <section>
|
|
674 <title>Device Drivers</title>
|
|
675 <para>
|
|
676 One of the things which became apparant after the infamous 'world' routine was
|
|
677 that this simple program could make use of any OS9 system peripherals provided
|
|
678 the drivers were there for it to use. This is also true of most OS9 software -
|
|
679 if you have the drivers any software can use it. For example, if you added a
|
|
680 hard drive to your system and called it H0, you could quite easily store
|
|
681 Stylograph on it, save your data on it, run the C compiler not to mention our
|
|
682 'Hello World' program. Unfortunatly, this isn't the case with DragonDOS which
|
|
683 is more or less tied to the pre-set Dragon hardware. So if you are planning to
|
|
684 add ram discs, hard discs etc. then OS9 is by far the easiest platform to
|
|
685 write software for. You will also probably find that writing software becomes
|
|
686 that much easier for OS9 once you have access to faster disk drives and maybe
|
|
687 an 80 column screen. It's by no means impossible to write stuff for DragonDOS
|
|
688 - just a lot harder thats all and I'll touch on this a bit later.
|
|
689 </para>
|
|
690 <para>
|
|
691 The key to this flexibility really comes down to device drivers and
|
|
692 descriptors on an OS9 system. Generally, any device you add will fall under
|
|
693 the category of disk - in which case an RBF device driver is required or
|
|
694 sequential (serial port, printer etc.) in which case an SCF device driver is
|
|
695 required.
|
|
696 </para>
|
|
697 <para>
|
|
698 Device drivers are just like any other OS9 program, they have a module header,
|
|
699 data area and code to perform to necessary operations. Here is a rough outline
|
|
700 of a device driver which is called DEVx:
|
|
701 <screen>
|
|
702 nam DEVx
|
|
703 ttl DEVx device driver
|
|
704
|
|
705 ifp1
|
|
706 use /h0/defs/os9defs
|
|
707 use /h0/defs/iodefs
|
|
708 use /h0/defs/scfdefs
|
|
709 endc
|
|
710
|
|
711 *data
|
|
712 org V.SCF
|
|
713 PST equ .
|
|
714
|
|
715 type equ DRIVR+OBJCT
|
|
716 revs equ REENT+1
|
|
717 mod PEND,PNAM,type,revs,PENT,PST
|
|
718
|
|
719 PNAM fcs /DEVx/
|
|
720
|
|
721 PENT lbra INIT
|
|
722 lbra READ
|
|
723 lbra WRITE
|
|
724 lbra GETSTA
|
|
725 lbra PUTSTA
|
|
726 lbra TERM
|
|
727
|
|
728 INIT .
|
|
729 READ .
|
|
730 WRITE .
|
|
731 GETSTA .
|
|
732 PUTSTA .
|
|
733 TERM .
|
|
734
|
|
735 emod
|
|
736 PEND equ *
|
|
737 </screen>
|
|
738 </para>
|
|
739 <para>
|
|
740 It follows the standard module structure, first the name and title of the
|
|
741 module NAM and TTL. Then the 'use' files are given. As well as os9defs we
|
|
742 include iodefs which contains i/o specific definitions, and because this just
|
|
743 happens to be an SCF device, scfdefs are included also. Next comes the data
|
|
744 area, however because the file managers (RBF or SCF) also require some data,
|
|
745 you should not start your data area off at 0 but the V.SCF equate (or DRVBEG
|
|
746 for RBF devices). No stack space is required here as the stack used will be
|
|
747 the one from the calling program. The PST label marks the datasize of the
|
|
748 module, and then the module header is given. Notice that the PRGRM equate is
|
|
749 replaced by the DRIVR equate indicating this is a device driver.
|
|
750 The PENT label marks the start of the driver. All OS9 drivers have 5 entry
|
|
751 points at the module start and LBRA instructions should always be used to call
|
|
752 these routines which are as follows:
|
|
753 </para>
|
|
754 <para>
|
|
755 INIT - called once on module start. Include any initialisation code required
|
|
756 to set your device up.
|
|
757 </para>
|
|
758 <para>
|
|
759 READ - called to read an 'item' of data from the device. Depending on the file
|
|
760 manager an 'item' can be anything from 1 character (SCF devices) to a disk
|
|
761 sector (RBF devices).
|
|
762 </para>
|
|
763 <para>
|
|
764 WRITE - called to write an 'item' of data to the device.
|
|
765 </para>
|
|
766 <para>
|
|
767 GETSTA - called to perform miscellaneous IO status routines. Since all IO
|
|
768 devices have different capabilities this provides a method to perform
|
|
769 operations which generally return some sort of status back to the caller - for
|
|
770 example the End Of File function (EOF) is implemented via a GetStat call.
|
|
771 </para>
|
|
772 <para>
|
|
773 SETSTA - similar to GetSta, but generally used to set some status information
|
|
774 on the device.
|
|
775 </para>
|
|
776 <para>
|
|
777 TERM - called once when the module is about to be removed from memory. Include
|
|
778 any de-allocation of memory/clean up routines etc. here.
|
|
779 </para>
|
|
780 <para>
|
|
781 Prior to showing a couple of example drivers, there are a few data structures
|
|
782 you need to be aware of when dealing with device drivers. Firstly, there is
|
|
783 your static storage, which can be thought of in similar terms to the data area
|
|
784 of a normal module. The static storage area for your device driver starts at
|
|
785 the V.SCF (or DRVBEG) position given in the previous header file, and
|
|
786 similarly to a normal OS9 program, the U register always points to this area.
|
|
787 Note, that unlike a module's data area, you should not use DP addressing on
|
|
788 data items. So, if you declare a variable as such:
|
|
789 <screen>
|
|
790 org V.SCF
|
|
791 flag rmb 1
|
|
792 </screen>
|
|
793 </para>
|
|
794 <para>
|
|
795 it can be accessed in any of the 5 procedures as:
|
|
796 <screen>
|
|
797 lda flag,u
|
|
798 </screen>
|
|
799 </para>
|
|
800 <para>
|
|
801 In addition to your own data held here, the system also stores some of it's
|
|
802 own static data just prior to the V.SCF (or DRVBEG) label which you can also
|
|
803 access. This consists of a 'common' area followed by a file manager specific
|
|
804 area. The exact format of these are listed in the System Programmers Guide
|
|
805 (and also in the iodefs,scf/rbfdefs files) but one of these items held in the
|
|
806 common area which the example drivers will make use of is named V.Port and
|
|
807 holds the address of the physical device in memory. How this gets set up, I
|
|
808 will cover later but suffice to say it enables the driver to access a physical
|
|
809 device in memory without having to know where it is exactly. As an example, if
|
|
810 you had an SCF device driver to talk to MC6821 PIAs and you had 4 of these
|
|
811 PIAs in your system you would only need 1 copy of the driver loaded - the
|
|
812 system would ensure V.Port is setup correctly depending on which PIA is being
|
|
813 accessed:
|
|
814 <screen>
|
|
815 lda #$FF A=255
|
|
816 ldx V.Port,u port address
|
|
817 sta 1,x port address+1=255
|
|
818 </screen>
|
|
819 </para>
|
|
820 <para>
|
|
821 The other data block you may need to be aware of is called the path
|
|
822 descriptor. This is a unique set of data for each open file or device in the
|
|
823 system. You cannot allocate your own data items here, but you can read & write
|
|
824 to data items here. Generally, the path descriptor holds information relevent
|
|
825 to the current open file or device (such as path numbers, file lengths etc.)
|
|
826 rather than constant device characteristics (port address etc.). Again, there
|
|
827 is a common part of the path descriptor, and then a file manager specific
|
|
828 area. The format of path descriptors is also listed in the System Programmers
|
|
829 Guide and can also be found in the iodefs,scf/rbfdefs files. Initially, you
|
|
830 will not probably need to utilise anything in the path descriptor, but if you
|
|
831 start writing more complex drivers you almost certainly will - generally the
|
|
832 file manager will provide most of the path descriptor management.
|
|
833 </para>
|
|
834 <para>
|
|
835 The path descriptor is pointed to by the Y register given in the READ, WRITE,
|
|
836 GETSTA & SETSTA procedures (remember, it only exists for an open file so will
|
|
837 not be accessable by the INIT & TERM routines) and as such can be accessed as
|
|
838 follows:
|
|
839 <screen>
|
|
840 lda PD.PD,y A=path number from path descriptor
|
|
841 </screen>
|
|
842 </para>
|
|
843 <para>
|
|
844 Last time I covered general device drivers. Next, are 2 example drivers, one
|
|
845 an SCF device, the other an RBF device.
|
|
846 </para>
|
|
847 </section>
|
|
848 <section>
|
|
849 <title>SCF Device Driver</title>
|
|
850 <para>
|
|
851 This device driver utilises an MC6821 PIA to perform character driven IO.
|
|
852 </para>
|
|
853 <para>
|
|
854 The first section is the common module header format:
|
|
855 <screen>
|
|
856 *******************************************
|
|
857 * PIA21
|
|
858 * Device Driver for MC6821 PIAs
|
|
859 *
|
|
860 * By J.Bird (c) 1992
|
|
861 *
|
|
862 * Uses Side B of MC6821 PIA
|
|
863 * CA2 & CB2 Control Strobe lines
|
|
864 * Non-interrupt driven
|
|
865 *
|
|
866
|
|
867 nam PIA21
|
|
868 ttl MC6821 PIA Device Driver
|
|
869
|
|
870 ifp1
|
|
871 use /h0/defs/os9defs
|
|
872 use /h0/defs/iodefs
|
|
873 use /h0/defs/scfdefs
|
|
874 endc
|
|
875
|
|
876 ***********************
|
|
877 * Edition History
|
|
878 *
|
|
879 * # date Comments
|
|
880 * - -------- -------------------------------
|
|
881 * 1 05.05.92 Driver first written. JRB
|
|
882 * 2 25.03.95 Update for Parallel Dragon link. JRB.
|
|
883
|
|
884 Revision equ 2
|
|
885
|
|
886 PBCREG equ %00101101
|
|
887 PBOUTP equ %11111111
|
|
888 PBINP equ %00000000
|
|
889
|
|
890 org V.SCF
|
|
891 PST equ .
|
|
892
|
|
893 mod PEND,PNAM,Drivr+Objct,Reent+Revision,PENT,PST
|
|
894 fcb READ.+WRITE.
|
|
895 PNAM fcs /PIA21/
|
|
896
|
|
897 PENT lbra INIT
|
|
898 lbra READ
|
|
899 lbra WRITE
|
|
900 lbra GETSTA
|
|
901 lbra PUTSTA
|
|
902 lbra TERM
|
|
903 </screen>
|
|
904 </para>
|
|
905 <para>
|
|
906 This defines an SCF man device called PIA21. On a sideline note, you may have
|
|
907 noticed the revision number as 2, and this is built into the module header.
|
|
908 This will ensure that if this module is loaded into memory and revision 1 is
|
|
909 also in memory (as part of the OS9 boot load for example) that revision 2 will
|
|
910 be used. Subsequently, a revision 3 would take precedence over 2 etc. etc.
|
|
911 </para>
|
|
912 <para>
|
|
913 The following two routines will be used throughout the code to configure the
|
|
914 port for either input or output. Note the use of the V.Port address from the
|
|
915 static storage to find the IO port's address.
|
|
916 <screen>
|
|
917 **********************
|
|
918 * SETOUT
|
|
919 * Set port dir for o/p
|
|
920 *
|
|
921 SETOUT ldx V.PORT,u load port address
|
|
922 clr 1,x clear control reg
|
|
923 lda #PBOUTP set port for o/p
|
|
924 sta ,x
|
|
925 lda #PBCREG reload ctrl reg
|
|
926 sta 1,x
|
|
927 clrb
|
|
928 rts
|
|
929
|
|
930 **********************
|
|
931 * SETIN ldx V.PORT,u
|
|
932 * Set port dir for i/p
|
|
933 *
|
|
934 SETIN ldx V.PORT,u
|
|
935 clr 1,x
|
|
936 lda #PBINP
|
|
937 sta ,x
|
|
938 lda #PBCREG
|
|
939 sta 1,x
|
|
940 clrb
|
|
941 rts
|
|
942 </screen>
|
|
943 </para>
|
|
944 <para>
|
|
945 The first of the device driver procedures INIT, just configures the port as
|
|
946 input and returns. All routines should return with B=0 and the carry bit clear
|
|
947 unless you wish to report an error, in which case the B register should
|
|
948 contain the OS9 error code. Note, that rts is used to return from a device
|
|
949 driver routine, not os9 f$exit as per normal OS9 programs.
|
|
950 <screen>
|
|
951 **********************
|
|
952 * INIT
|
|
953 * Entry: U = Static storage
|
|
954 * Setup the PIA
|
|
955 *
|
|
956 INIT bsr setin
|
|
957 clrb
|
|
958 rts
|
|
959 </screen>
|
|
960 </para>
|
|
961 <para>
|
|
962 The READ routine is required to fetch a data item from the IO port. For SCF
|
|
963 type devices, this is 1 character and should be returned in the A register.
|
|
964 <screen>
|
|
965 **********************
|
|
966 * READ
|
|
967 * Entry: U = Static Storage
|
|
968 * Y = Path Descriptor
|
|
969 * Exit: A = Character read
|
|
970 *
|
|
971 * Read a byte from Port B
|
|
972 *
|
|
973 READ ldx V.PORT,u load port address
|
|
974 readlp tst 1,x test for data ready
|
|
975 bmi readbyte
|
|
976 pshs x sleep for a bit if not
|
|
977 ldx #10
|
|
978 os9 f$sleep
|
|
979 puls x
|
|
980 bra readlp
|
|
981 readbyte lda ,x read byte
|
|
982 sta ,x issue strobe
|
|
983 clrb
|
|
984 rts
|
|
985 </screen>
|
|
986 </para>
|
|
987 <para>
|
|
988 The READ routine makes use of another OS9 system call - sleep. On entry, X
|
|
989 should contain the number of clock cycles to sleep for (clock cycle = 1/50
|
|
990 second on the Dragon). This is not strictly required, however when an IO
|
|
991 request is performed OS9 prevents any other process from running until it
|
|
992 completes or executes a sleep request. For example, if you had a process
|
|
993 running as a background task using this device and the device stalled (ie. it
|
|
994 never became ready) your machine would lockup. By sleeping for a short while
|
|
995 between checks you ensure this cannot happen.
|
|
996 </para>
|
|
997 <para>
|
|
998 The WRITE routine acts in a similar way to READ, except the character passed
|
|
999 in the A register should be written to the port:
|
|
1000 <screen>
|
|
1001 **********************
|
|
1002 * WRITE
|
|
1003 * Entry: U = Static storage
|
|
1004 * Y = Path Descriptor
|
|
1005 * A = Char to write
|
|
1006 *
|
|
1007 * Write a byte to Port B
|
|
1008 *
|
|
1009 WRITE ldx V.PORT,U load port address
|
|
1010 sta ,x write byte
|
|
1011 writlp tst 1,x wait for ack
|
|
1012 bmi wrt
|
|
1013 pshs x
|
|
1014 ldx #1
|
|
1015 os9 f$sleep
|
|
1016 puls x
|
|
1017 bra writlp
|
|
1018 wrt lda ,x clear control reg
|
|
1019 ok clrb
|
|
1020 rts
|
|
1021 </screen>
|
|
1022 </para>
|
|
1023 <para>
|
|
1024 The next two routines demonstrate device driver usage of the GetStat and
|
|
1025 SetStat calls and also the path descriptor.
|
|
1026 </para>
|
|
1027 <para>
|
|
1028 There are a few predefined GetStat calls under OS9 which nearly all SCF device
|
|
1029 drivers will have to deal with. These are Test for Data Ready and Test for End
|
|
1030 Of File. The predefined GetStat codes are defined in the OS9Defs file but be
|
|
1031 warned: a lot are device dependent and will not be implemented. Generally,
|
|
1032 only the ones listed above are available on all SCF devices.
|
|
1033 </para>
|
|
1034 <para>
|
|
1035 GetStat calls are made by the caller by issuing an OS9 i$getstt call with the
|
|
1036 A register containing the path number and the B register containing the
|
|
1037 GetStat code - the other register contents depend on the GetStat call being
|
|
1038 made. This example program loops until data is available from the keyboard:
|
|
1039 <screen>
|
|
1040 chklp clra pathnum of stdin
|
|
1041 ldb #SS.Ready getstat code
|
|
1042 os9 i$getstt issue call
|
|
1043 bcc okay if c bit clear data is rdy
|
|
1044 cmpb #E$NRDY otherwise check the code
|
|
1045 bne error returned was not ready
|
|
1046 bra chklp
|
|
1047 </screen>
|
|
1048 </para>
|
|
1049 <para>
|
|
1050 In order to write a routine to process a GetStat call, you need to access the
|
|
1051 contents of the registers when the call was made - in this case the B register
|
|
1052 to obtain the getstat code. Within the common part of the path descriptor is a
|
|
1053 pointer to a data area which contains all the register contents when the call
|
|
1054 was made. Labels set up in the os9defs file make accessing this information
|
|
1055 easy.
|
|
1056 </para>
|
|
1057 <para>
|
|
1058 The following GetStat routine processes 3 codes. It first retrieves the
|
|
1059 register pointer from the path descriptor then loads what was in the B
|
|
1060 register into the A register using the R$B label to find the location. The 3
|
|
1061 getstat codes are SS.Ready - test for data ready which is performed by using
|
|
1062 the PIA to determine if a data byte is waiting to be processed or not, SS.EOF
|
|
1063 - end of file which for this device has no meaning and therefore always
|
|
1064 returns with no error ie. not end of file. The final code is a user defined
|
|
1065 one I created specific for this device named SS.VSupv. When called with this
|
|
1066 code it forces the port to change direction to input using the 'setin'
|
|
1067 procedure defined earlier. All other codes return an error.
|
|
1068 <screen>
|
|
1069 **********************
|
|
1070 * GETSTA
|
|
1071 * U = Static Storage
|
|
1072 * Y = Path Descriptor
|
|
1073 *
|
|
1074 GETSTA ldx PD.Rgs,y X=pointer to registers
|
|
1075 lda R$B,x A=contents of B reg.
|
|
1076 cmpa #SS.Ready check for ready code
|
|
1077 bne gsta1 if not, check next
|
|
1078 ldx V.Port,u use the PIA to determine
|
|
1079 tst 1,x if data is available
|
|
1080 bmi ok
|
|
1081 ldb #E$NotRdy
|
|
1082 coma sets the carry flag
|
|
1083 rts
|
|
1084 gsta1 cmpa #SS.EOF check for eof code
|
|
1085 beq ok which always returns ok
|
|
1086 cmpa #SS.VSupv check for a special code
|
|
1087 beq setin which changes port dir
|
|
1088 comb otherwise return
|
|
1089 ldb #E$UnkSVC unknown code error
|
|
1090 rts
|
|
1091 </screen>
|
|
1092 </para>
|
|
1093 <para>
|
|
1094 The SetSta call works in an identical way to GetStat, and generally SCF
|
|
1095 devices do not use SetStat calls so you can just return the E$UnkSVC error
|
|
1096 immediatly. For our example driver, SetStat supports the new SS.VSupv code
|
|
1097 which when received will force the port direction to be output using the
|
|
1098 'setout' procedure:
|
|
1099 <screen>
|
|
1100 **********************
|
|
1101 * PUTSTA
|
|
1102 * Supv request sets port to o/p
|
|
1103 *
|
|
1104 PUTSTA ldx PD.Rgs,y X=register stack
|
|
1105 ldb R$B,x B=contents of reg B
|
|
1106 cmpb #SS.VSupv check for recognized code
|
|
1107 beq setout and process it
|
|
1108 comb otherwise return error.
|
|
1109 ldb #E$UnkSVC
|
|
1110 rts
|
|
1111 </screen>
|
|
1112 </para>
|
|
1113 <para>
|
|
1114 Finally, the TERM routine. Since the driver has not allocated any extra memory
|
|
1115 or anything it can just return okay:
|
|
1116 <screen>
|
|
1117 **********************
|
|
1118 * TERM
|
|
1119 * Terminate Driver
|
|
1120 *
|
|
1121 TERM clrb
|
|
1122 rts
|
|
1123
|
|
1124 emod
|
|
1125 PEND equ *
|
|
1126 </screen>
|
|
1127 </para>
|
|
1128 <para>
|
|
1129 Thats just about it.
|
|
1130 </para>
|
|
1131 <para>
|
|
1132 Before proceeding to an RBF device driver there is one more thing required to
|
|
1133 complete the new SCF device - a device descriptor.
|
|
1134 </para>
|
|
1135 <para>
|
|
1136 The device descriptor is just composed of a set of data items which define the
|
|
1137 device you are creating. In particular, it is the name you refer to when you
|
|
1138 want to access the device - so our example is called P1 so in order to access
|
|
1139 it you just refer to this device ie.
|
|
1140 <screen>
|
|
1141 list startup >/p1
|
|
1142 </screen>
|
|
1143 </para>
|
|
1144 <para>
|
|
1145 copies the startup file to device /p1. The descriptor also contains information
|
|
1146 to identify it's device driver, file manager and other unique information. It
|
|
1147 also contains the port address (which is copied to V.Port so your driver can
|
|
1148 refer to it) and in the case of SCF devices contains a whole list of attributes
|
|
1149 referring to the capabilities of the device.
|
|
1150 </para>
|
|
1151 <para>
|
|
1152 Here is the PIA device descriptor to go with the PIA21 device driver:
|
|
1153 <screen>
|
|
1154 ****************************************
|
|
1155 * PIA Descriptor module
|
|
1156 *
|
|
1157 * Source by J.Bird (C) 1992
|
|
1158 *
|
|
1159 ifp1
|
|
1160 use /h0/defs/os9defs
|
|
1161 endc
|
|
1162
|
|
1163 nam P1
|
|
1164 ttl PIA Device Descriptor
|
|
1165
|
|
1166 mod PEND,PNAM,DEVIC+OBJCT,REENT+1,PMGR,PDRV
|
|
1167
|
|
1168 fcb READ.+WRITE.+SHARE.
|
|
1169 fcb $FF IOBlock (unused)
|
|
1170 fdb $FF32 hardware address
|
|
1171 fcb PNAM-*-1 option byte count
|
|
1172 fcb $0 SCF device
|
|
1173 fcb $0 Case (upper & lower)
|
|
1174 fcb $1 Erase on backspace
|
|
1175 fcb $0 delete (BSE over line)
|
|
1176 fcb $0 echo off
|
|
1177 fcb $1 lf on
|
|
1178 fcb $0 eol null count
|
|
1179 fcb $0 no pause
|
|
1180 fcb 24 lines per page
|
|
1181 fcb $8 backspace
|
|
1182 fcb $18 delete line char
|
|
1183 fcb $0D end of record
|
|
1184 fcb $1b eof
|
|
1185 fcb $04 reprint line char
|
|
1186 fcb $01 duplicate last line char
|
|
1187 fcb $17 pause char
|
|
1188 fcb $03 interrupt char
|
|
1189 fcb $05 quit char
|
|
1190 fcb $08 backspace echo char
|
|
1191 fcb $07 bell
|
|
1192 fcb $00 n/a
|
|
1193 fcb $00 n/a
|
|
1194 fdb pnam offset to name
|
|
1195 fdb $0000 offset to status routine
|
|
1196 pnam fcs "P1"
|
|
1197 pmgr fcs "SCF"
|
|
1198 pdrv fcs "PIA21"
|
|
1199 emod
|
|
1200 pend equ *
|
|
1201 end
|
|
1202 </screen>
|
|
1203 </para>
|
|
1204 <para>
|
|
1205 Once again it's format is similiar to any OS9 module, with a program type of
|
|
1206 DEVIC indicating device descriptor. The module just consists of fcb data
|
|
1207 stataments. Generally, this may well suffice for any SCF module you care to
|
|
1208 create. Some key features to note however are:
|
|
1209 <screen>
|
|
1210 fcb READ.+WRITE.+SHARE.
|
|
1211 </screen>
|
|
1212 </para>
|
|
1213 <para>
|
|
1214 indicates the device can be read, written and also multiple processes can
|
|
1215 access it at any one time. Failing to specify some of these will cause the
|
|
1216 system to return 'device not capable of operation' type errors.
|
|
1217 <screen>
|
|
1218 fdb $FF32 hardware address
|
|
1219 </screen>
|
|
1220 </para>
|
|
1221 <para>
|
|
1222 The physical hardware port address. This indicates our PIA sits at $FF32 in
|
|
1223 memory - copied to V.Port in the device driver.
|
|
1224 <screen>
|
|
1225 fcb $0 echo off
|
|
1226 </screen>
|
|
1227 </para>
|
|
1228 <para>
|
|
1229 This is an important one. If set to non-zero when the SCF file manager reads a
|
|
1230 byte it will automatically send it back out using the write routine. In the
|
|
1231 case of our 6821 driver which only operates in one direction at a time this is
|
|
1232 not a good idea.
|
|
1233 <screen>
|
|
1234 pnam fcs "P1"
|
|
1235 pmgr fcs "SCF"
|
|
1236 pdrv fcs "PIA21"
|
|
1237 </screen>
|
|
1238 </para>
|
|
1239 <para>
|
|
1240 Finally, these give the device descriptor name (pnam), its associated file
|
|
1241 manager (pmgr) and device driver (pdrv) and is the only way the system has of
|
|
1242 working this out.
|
|
1243 </para>
|
|
1244 <para>
|
|
1245 As an additional note, the device driver INIT also sets the Y register to point
|
|
1246 to the device descriptor data should you need to access it. Most of the
|
|
1247 parameters are also copied into the path descriptor option section where you
|
|
1248 can access them if required. Remember, any modifications you make to the path
|
|
1249 descriptor locations will only be valid for the duration the device or file is
|
|
1250 'open'.
|
|
1251 </para>
|
|
1252 <para>
|
|
1253 Once you have your new driver and descriptor you just load them into memory and
|
|
1254 then any OS9 program can use them:
|
|
1255 <screen>
|
|
1256 load pia21
|
|
1257 load p1
|
|
1258 list startup >/p
|
|
1259 </screen>
|
|
1260 </para>
|
|
1261 </section>
|
|
1262 <section>
|
|
1263 <title>RBF Device Driver</title>
|
|
1264 <para>
|
|
1265 RBF drivers tend to be a little more complex than SCF ones but not drastically
|
|
1266 so. In addition, practically everything covered in the SCF device also applies
|
|
1267 to RBF device covered here. They also tend to open up your system a lot more -
|
|
1268 essentially you are getting another disk drive out of it. The example I am
|
|
1269 using here is based around the
|
|
1270 <ulink url="http://www.onastick.clara.net/rdisk.htm">RAM disc design</ulink>
|
|
1271 I wrote a couple of years ago in Up2Date.
|
|
1272 </para>
|
|
1273 <para>
|
|
1274 The RAM disc design used once again an MC6821 PIA, which utilised one side to
|
|
1275 select a RAM function to be performed and the other side as the data bus. The
|
|
1276 design was specifically built to make it easy to interface into an OS9 system.
|
|
1277 </para>
|
|
1278 <para>
|
|
1279 For RBF devices, the read and write functions are required to transfer a 256
|
|
1280 byte sector from a given 24 bit logical sector number (LSN). LSNs give a means
|
|
1281 of addressing disks without having to worry about tracks/sectors per track.
|
|
1282 They are numbered from 0 upwards starting (on a 'real' disk anyway) as LSN
|
|
1283 0=track 0, sector 1 etc. On a standard Dragon disk (40T/SS) LSN 0=Track 0,
|
|
1284 sector 1, LSN 1=Track 0, sector 2, LSN 18=Track 1, sector 1 etc.
|
|
1285 </para>
|
|
1286 <para>
|
|
1287 The PIA design allowed for the following operations:
|
|
1288 </para>
|
|
1289 <para>
|
|
1290 1. Write MSB of LSN. Write the top 16 bits of the LSN to the RAM drive (the top
|
|
1291 8 bits are discarded as you would need a phenominal amount of RAM to need these
|
|
1292 bits).
|
|
1293 </para>
|
|
1294 <para>
|
|
1295 2. Write LSN of LSN. Write the botton 8 bits of the LSN to the RAM drive.
|
|
1296 </para>
|
|
1297 <para>
|
|
1298 3. Read a sector. Transfer the sector byte by byte from the RAM drive hardware
|
|
1299 to a buffer.
|
|
1300 </para>
|
|
1301 <para>
|
|
1302 4. Write a sector. Transfer a sector byte by byte from a buffer to the RAM
|
|
1303 drive hardware.
|
|
1304 </para>
|
|
1305 <para>
|
|
1306 So, there is really minimal processing involved to utilise the RAM drives. Most
|
|
1307 floppy, or hard disk drivers would normally require some LSN to track/sector
|
|
1308 conversion.
|
|
1309 </para>
|
|
1310 <para>
|
|
1311 All RBF device drivers differ subtly from SCF drivers in that there are
|
|
1312 certain mandatory things you must perform within them in order for them to
|
|
1313 function correctly. I will attempt to explain these throughout the driver
|
|
1314 example.
|
|
1315 </para>
|
|
1316 <para>
|
|
1317 An RBF driver starts off like all OS9 modules, with the standard header
|
|
1318 format. Here you see I am up to revision 6 of the PIA RAM disk driver:
|
|
1319 <screen>
|
|
1320 *********************************************
|
|
1321 * Rdisk
|
|
1322 * A driver for a Ram disk!
|
|
1323 * By G.Twist (c) 1986
|
|
1324 * modified by Bernd H. Neuner 1987
|
|
1325 * Version for a totally different Ram disk!
|
|
1326 *
|
|
1327 * By J.B. & O.B. (C) 1991,1992
|
|
1328 *
|
|
1329
|
|
1330 nam Rdisk
|
|
1331 ttl A Device Driver for a RAM Disk
|
|
1332
|
|
1333 ifp1
|
|
1334 use /d0/defs/os9defs
|
|
1335 use /d0/defs/iodefs
|
|
1336 use /d0/defs/rbfdefs
|
|
1337 endc
|
|
1338
|
|
1339 ***********************
|
|
1340 * Edition History
|
|
1341
|
|
1342 * # date Comments
|
|
1343 * -- -------- ----------------------------------------------
|
|
1344 * 1 86/12/17 Driver first developed GDT
|
|
1345 * 2 86/12/18 work to fix minor access bugs GDT
|
|
1346 * 3 30.11.87 bug in COPY routine fixed. BHN (no more error 214 now.)
|
|
1347 * 4 31.12.91 Test version for main RAM JB/OB
|
|
1348 * 5 08.01.92 Driver for 128K PIA RAM JB/OB
|
|
1349 * 6 18.09.92 Up-issue to support up to 512K JB
|
|
1350
|
|
1351 Revision equ 6
|
|
1352 NumDrvs set 1 Number of drives
|
|
1353
|
|
1354 * pia control comands
|
|
1355 pia.ddr equ %00111000
|
|
1356 pia.off equ %00111100
|
|
1357 pia.act equ %00101100
|
|
1358 ext.msr equ %00000001
|
|
1359 ext.lsr equ %00001000
|
|
1360 ext.read equ %00000010
|
|
1361 ext.writ equ %00000110
|
|
1362 output equ %11111111
|
|
1363 outb equ %00001111
|
|
1364 pia.iora equ 0
|
|
1365 pia.cnra equ 1
|
|
1366 pia.iorb equ 2
|
|
1367 pia.cnrb equ 3
|
|
1368
|
|
1369
|
|
1370 org Drvbeg
|
|
1371 rmb NumDrvs*DrvMem
|
|
1372 LSNZERO rmb 1
|
|
1373 RAMSTA equ .
|
|
1374
|
|
1375 mod RAMEND,RAMNAM,Drivr+Objct,Reent+Revision,RAMENT,RAMSTA
|
|
1376 RAMNAM fcs /Rdisk/
|
|
1377
|
|
1378 RAMENT lbra INIT
|
|
1379 lbra READ
|
|
1380 lbra WRITE
|
|
1381 lbra GETSTA
|
|
1382 lbra PUTSTA
|
|
1383 lbra TERM
|
|
1384 </screen>
|
|
1385 </para>
|
|
1386 <para>
|
|
1387 Along with the equates needed for the driver, the RBF driver static storage is
|
|
1388 being utilised (note the V.SCF equate replaced with DRVBEG). The first
|
|
1389 mandatory thing about an RBF driver is that you must allocate a drive table
|
|
1390 for EACH drive you plan the driver to deal with. The drive table will hold
|
|
1391 information particular to the drive (such as number of sectors/track etc.) and
|
|
1392 must be the first block of data in your static storage. You should reserve
|
|
1393 DRVMEM bytes (DRVMEM is defined in the rbfdefs file) multiplied by the number
|
|
1394 of drives your driver can handle (in this case NumDrvs is defined as 1). So
|
|
1395 the DDISK OS9 driver reserves 4 times this amount to handle the 4 possible
|
|
1396 floppy disks on a Dragon system. The only data item declared for our use is
|
|
1397 the LSNZERO byte.
|
|
1398 </para>
|
|
1399 <para>
|
|
1400 The INIT routine of the driver also requires some mandatory code required by
|
|
1401 all RBF drivers:
|
|
1402 </para>
|
|
1403 <para>
|
|
1404 1. Initialise the V.NDRV variable of the RBF static storage to the number of
|
|
1405 drives the driver will deal with.
|
|
1406 </para>
|
|
1407 <para>
|
|
1408 2. Initialise the DD.TOT and V.Trak variables held within the drive tables of
|
|
1409 the static storage of EACH drive to a non-zero value.
|
|
1410 </para>
|
|
1411 <para>
|
|
1412 In this driver, although it is only targetted for 1 drive, there is a loop
|
|
1413 construct present for dealing with multiple drives should this ever be a
|
|
1414 requirement. In addition, our INIT routine also sets up the PIA and
|
|
1415 initialises our own data item.
|
|
1416 <screen>
|
|
1417 *****************************
|
|
1418 * INIT
|
|
1419 * Set up the ramdisk
|
|
1420
|
|
1421 INIT lda #NumDrvs Set no drives to 1
|
|
1422 sta V.NDRV,U
|
|
1423 clr LSNZERO,U Initialise our data
|
|
1424 lda #$FF non-zero value
|
|
1425 leax DrvBeg,U X=Start of drive table
|
|
1426 initdrv
|
|
1427 sta DD.Tot,X write in non-zero values
|
|
1428 sta V.Trak,X
|
|
1429 leax DrvMem,x
|
|
1430 deca loop through drives
|
|
1431 bne initdrv
|
|
1432 * set up pia
|
|
1433 ldx V.PORT,U
|
|
1434 lda #pia.off
|
|
1435 sta pia.cnra,x select a side off
|
|
1436 lda #pia.ddr
|
|
1437 sta pia.cnrb,x select b side ddr
|
|
1438 lda #outb
|
|
1439 sta pia.iorb,x set ls 4 bits to outputs
|
|
1440 lda #pia.off
|
|
1441 sta pia.cnrb,x select b side io reg
|
|
1442 clr pia.iorb,x
|
|
1443 clrb
|
|
1444 INITXIT rts
|
|
1445 </screen>
|
|
1446 </para>
|
|
1447 <para>
|
|
1448 The READ routine is required to transfer an entire disk sector to a buffer.
|
|
1449 The 24 bit LSN number to read is held in the B and X registers as follows:
|
|
1450 <screen>
|
|
1451 bits#
|
|
1452 23 ... 15 ... 7 ... 0
|
|
1453 | B | X |
|
|
1454 </screen>
|
|
1455 </para>
|
|
1456 <para>
|
|
1457 The VALID procedure actually performs the sector validation and loading of
|
|
1458 this data into the PIAs. The sector is then transfered into the sector buffer
|
|
1459 pointed to by the PD.BUF location in the path descriptor.
|
|
1460 </para>
|
|
1461 <para>
|
|
1462 The READ routine (surprisingly enough) also has to perform some mandatory
|
|
1463 operations - in particular when LSN 0 is read. This contains disk ID
|
|
1464 information and whenever a request is made to read this sector, the driver is
|
|
1465 required to copy DD.SIZ bytes into the drive table for the required drive:
|
|
1466 <screen>
|
|
1467 *****************************
|
|
1468 * READ
|
|
1469 * read a sector from disk
|
|
1470 * Entry: U = Static Storage
|
|
1471 * Y = Path Descriptor
|
|
1472 * B = MSB of LSN
|
|
1473 * X = LSB's of LSN
|
|
1474 * Exit: 256 byte sector in PD.BUF buffer
|
|
1475 *
|
|
1476 READ clr LSNZERO,U init LSNZERO flag
|
|
1477 bsr VALID validate the LSN supplied
|
|
1478 bcs READ99 raise error if invalid
|
|
1479 pshs Y preserve pointer to PD
|
|
1480 ldy PD.BUF,Y Y=start of sector buffer
|
|
1481 ldx V.PORT,U X=PIA port address
|
|
1482 lda #pia.ddr program PIA for transfer
|
|
1483 sta pia.cnra,x
|
|
1484 clr pia.iora,x
|
|
1485 lda #pia.act
|
|
1486 sta pia.cnra,x
|
|
1487 lda #ext.read
|
|
1488 sta pia.iorb,x
|
|
1489 leax pia.iora,x
|
|
1490 clrb
|
|
1491 READS10 lda ,x transfer sector from PIA
|
|
1492 sta ,y+
|
|
1493 decb
|
|
1494 bne READS10
|
|
1495 leax V.Port,u reset PIA
|
|
1496 clr pia.iorb,x
|
|
1497 lda #pia.off
|
|
1498 sta pia.cnra,x
|
|
1499 puls Y
|
|
1500 *mandatory RBF code - copy LSN 0
|
|
1501 tst LSNZERO,U VALID routine will set this
|
|
1502 beq READ90 if LSN 0 specified
|
|
1503 lda PD.DRV,Y extract drive num
|
|
1504 ldb #DRVMEM size of drive table
|
|
1505 mul
|
|
1506 leax DRVBEG,U start of drive tables
|
|
1507 leax D,X add on drive offset
|
|
1508 ldy PD.BUF,Y copy DD.Siz bytes
|
|
1509 ldb #DD.SIZ-1 into drive table
|
|
1510 COPLSN0 lda B,Y
|
|
1511 sta B,X
|
|
1512 decb
|
|
1513 bpl COPLSN0
|
|
1514 READ90 clrb
|
|
1515 READ99 rts
|
|
1516 </screen>
|
|
1517 </para>
|
|
1518 <para>
|
|
1519 The sector write routine works in a similar manner: the LSN is supplied in the
|
|
1520 same format, and this time the sector held in PD.BUF must be transferred to
|
|
1521 the RAM disk PIAs. No special processing is required for LSN 0.
|
|
1522 <screen>
|
|
1523 *****************************
|
|
1524 * WRITE
|
|
1525 * Write a sector to disk
|
|
1526 * Entry: U = Static Storage
|
|
1527 * Y = Path Descriptor
|
|
1528 * B = MSB of LSN
|
|
1529 * X = LSB's of LSN
|
|
1530 * PD.Buf = Sector to write
|
|
1531 *
|
|
1532 WRITE bsr VALID validate sector
|
|
1533 bcs WRIT99
|
|
1534 WRITS pshs Y save PD
|
|
1535 ldy PD.BUF,Y Y=sector buffer
|
|
1536 ldx V.PORT,U X=port address
|
|
1537 lda #pia.ddr program PIA
|
|
1538 sta pia.cnra,x for write txfr
|
|
1539 lda #output
|
|
1540 sta pia.iora,x
|
|
1541 lda #pia.act
|
|
1542 sta pia.cnra,x
|
|
1543 lda #ext.writ
|
|
1544 sta pia.iorb,x
|
|
1545 leax pia.iora,x
|
|
1546 clrb
|
|
1547 WRITS10 lda ,y+ transfer sector
|
|
1548 sta ,x to PIA
|
|
1549 lda ,x
|
|
1550 decb
|
|
1551 bne WRITS10
|
|
1552 ldx V.PORT,U restore PIA
|
|
1553 clr pia.iorb,x
|
|
1554 lda #pia.off
|
|
1555 sta pia.cnra,x
|
|
1556 puls Y
|
|
1557 clrb
|
|
1558 WRIT99 rts
|
|
1559 </screen>
|
|
1560 </para>
|
|
1561 <para>
|
|
1562 The VALID subroutine is shown below. This serves to program the PIA with the
|
|
1563 LSN supplied - note the top part of the LSN held in the B register is
|
|
1564 discarded for this driver, only the X part is used.
|
|
1565 <screen>
|
|
1566 *****************************
|
|
1567 * VALID
|
|
1568 * validate a sector
|
|
1569 * and set up external registers
|
|
1570 * to reqired page in ram
|
|
1571 *
|
|
1572 VALID
|
|
1573 cmpx #$0000 check for LSN 0
|
|
1574 bne NOTLSN0 set flag appropriatly
|
|
1575 inc LSNZERO,U
|
|
1576 NOTLSN0 pshs y
|
|
1577 ldy V.PORT,U
|
|
1578 lda #pia.ddr select direction reg
|
|
1579 sta pia.cnra,y
|
|
1580 lda #output set bits to output
|
|
1581 sta pia.iora,y
|
|
1582 lda #pia.act enable ca2 strobe
|
|
1583 sta pia.cnra,y
|
|
1584 lda #ext.msr select ms sector reg
|
|
1585 sta pia.iorb,y
|
|
1586 tfr x,d
|
|
1587 sta pia.iora,y write ms value
|
|
1588 lda pia.iora,y do the read (strobe)
|
|
1589 lda #ext.lsr select ls sector reg
|
|
1590 sta pia.iorb,y
|
|
1591 stb pia.iora,y write ls value
|
|
1592 ldb pia.iora,y do the read (strobe)
|
|
1593 clr pia.iorb,y select nothing
|
|
1594 puls y note a side still set for output
|
|
1595 clrb
|
|
1596 rts
|
|
1597 </screen>
|
|
1598 </para>
|
|
1599 <para>
|
|
1600 On the GETSTA and SETSTA side of things, there are no 'mandatory' getsta codes
|
|
1601 you need to deal with and two SETSTA codes: SS.RST (restore head to track 0)
|
|
1602 and SS.WRT (write track). Restore head to track 0 serves no useful purpose on
|
|
1603 a RAM disc and can just return okay. The SS.WRT call is used during format
|
|
1604 operations to actually format the track - again for a RAM drive this will not
|
|
1605 be required and can just return okay. All others return with an error:
|
|
1606 <screen>
|
|
1607 **************************
|
|
1608 * GETSTA
|
|
1609 * get device status
|
|
1610 *
|
|
1611 GETSTA
|
|
1612 Unknown comb
|
|
1613 ldb #E$UnkSVC
|
|
1614 rts
|
|
1615
|
|
1616 **************************
|
|
1617 * PUTSTA
|
|
1618 * Set device Status
|
|
1619 *
|
|
1620 PUTSTA cmpb #SS.Reset
|
|
1621 beq PUTSTA90
|
|
1622 cmpb #SS.WTrk
|
|
1623 bne Unknown
|
|
1624
|
|
1625 PUTSTA90 clrb
|
|
1626 rts
|
|
1627 </screen>
|
|
1628 </para>
|
|
1629 <para>
|
|
1630 Finally, the TERM routine simply exits cleanly:
|
|
1631 <screen>
|
|
1632 *****************************
|
|
1633 * TERM
|
|
1634 * terminate Driver
|
|
1635 *
|
|
1636 TERM clrb
|
|
1637 rts
|
|
1638
|
|
1639 emod
|
|
1640 RAMEND equ *
|
|
1641 </screen>
|
|
1642 </para>
|
|
1643 <para>
|
|
1644 All that remains to cover is the RBF device descriptor, which is actually a
|
|
1645 lot simpler than the SCF one:
|
|
1646 <screen>
|
|
1647 ******************************************
|
|
1648 * RamDisk Discriptor module
|
|
1649 *
|
|
1650 *
|
|
1651 ifp1
|
|
1652 use /d0/defs/os9defs
|
|
1653 endc
|
|
1654
|
|
1655 nam R0
|
|
1656 ttl Drive Discriptor module
|
|
1657
|
|
1658 mod DEnd,DNam,DEVIC+OBJCT,REENT+1,DMgr,DDrv
|
|
1659
|
|
1660 fcb DIR.+SHARE.+PREAD.+PWRIT.+UPDAT.+EXEC.+PEXEC.
|
|
1661 fcb $FF IOBlock (unused)
|
|
1662 fdb $FF34 hardware address
|
|
1663 fcb DNam-*-1 option byte count
|
|
1664 fcb $1 Rbf device
|
|
1665 fcb 0 Drive number
|
|
1666 fcb 03 6ms Step rate
|
|
1667 fcb $80 Standard OS9 Winchester drive
|
|
1668 fcb 0 Single density
|
|
1669 fdb 4 number of tracks
|
|
1670 fcb 1 number of sides
|
|
1671 fcb 1 dont verify any writes
|
|
1672 fdb 256 sectors per track
|
|
1673 fdb 256 sectors on track 0, side 0
|
|
1674 fcb 1 sector interleave factor
|
|
1675 fcb 1 sector allocation size
|
|
1676 DNam fcs "R0"
|
|
1677 DMgr fcs "RBF"
|
|
1678 DDrv fcs "RDisk"
|
|
1679 emod
|
|
1680 DEnd equ *
|
|
1681 end
|
|
1682 </screen>
|
|
1683 </para>
|
|
1684 <para>
|
|
1685 This is the device descriptor named R0 for our RAM drive. It shares similar
|
|
1686 properties to the SCF one, firstly the attributes byte indicating full access
|
|
1687 (READ.+WRITE. etc. ) including directory access. There is also the familiar
|
|
1688 hardware address and at the end the device descriptor name, file manager name
|
|
1689 and driver name. Most of the descriptor is composed of the drive attributes:
|
|
1690 drive number, total tracks, sectors per track etc. Since this is a RAM drive
|
|
1691 all I have done is ensured the total sectors on the media is equal to the
|
|
1692 amount of RAM I have available - in this case 256K.
|
|
1693 </para>
|
|
1694 <para>
|
|
1695 Once assembled, you can simply load and access the RAM drive like any other
|
|
1696 disk on your system:
|
|
1697 <screen>
|
|
1698 load rdisk
|
|
1699 load r0
|
|
1700 chd /r0
|
|
1701 copy /d0/startup /r0/startup #32k
|
|
1702
|
|
1703 etc.
|
|
1704 </screen>
|
|
1705 </para>
|
|
1706 <para>
|
|
1707 That just about wraps it up. A couple of other points worth noting: it's
|
|
1708 normally worth either checking out or basing your driver on someone else's as
|
|
1709 a good starting point, there is also a fair deal of information around
|
|
1710 particularly from the CoCo side of things. And something I'd always recommend:
|
|
1711 as you have seen is nearly always easier to debug stuff under normal DragonDOS
|
|
1712 so I'd suggest at least prototyping your code under this environment to see if
|
|
1713 at least the logic works how you think it should. That way, when you port it
|
|
1714 to OS9 all you have to worry about is the quirks of OS9 rather than your own
|
|
1715 driver logic.
|
|
1716 </para>
|
|
1717 </section>
|
|
1718 </article>
|