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