Amiga Machine Language (Chapter 6)
Innen: amigaspirit.hu - pegasos.hu Wiki
Ugrás a navigációhozUgrás a kereséshez
Chapter 6 --------- 6.The Operating System. ----------------------- Now lets take a step forward in your ability to write assembly language programs.Its not enough to put a piece of text in memory someplace.You want to be able to put it on the screen.Do you know how to write a character on the screen?Do you know how to draw a window on the screen that can be modified by the mouse?Actually you don't have to have terribly precise knowledge about such topics. Fortunately,the Amigas operating system supplies routines that take care of common tasks like this.It can seem quite complicated due to the number of routines necessary.These routines are in libraries.We'll look at the libraries in some depth now.
6.1.Load Libraries. ------------------- Before you can use a library,it must be available.It has to be loaded into memory.Unfortunately,the whole library must be loaded, even if you only need one of the functions. First you need to decide what the program must be able to do,so you can see which libraries you'll need.For simple I/O text,you don't need a library that contains routines for moving graphics! There are a number of libraries onj a normal Workbench disk.Heres an overview of the names and the sort of functions they do: Exec.Library; This library is needed to load the other libraries.It is already in memory and doesn't need to be loaded.Its in charge of basic functions like reserving memory and working with I/O channels. Dos.Library; Contains all the functions for normal I/O operations,for instance screen or disk access. Intuition.Library; Used for working with screens,windows,menus,etc... Clist.Library; This contains routines for working with the Copper lists that are used for controlling the screen. Console.Library; Contains graphics routines for text output in console windows. Diskfont.Library; Used for working with the character fonts that are stored on the disk. Graphics.Library; This library contains functions to control the Blitter(or graphics )chip.Its used for basic graphics functions. Icon.Library; Used in the development and use of workbench symbols(icons).
Layers.Library; Used for working with screen memory (layers).
Mathffp.Library; Contains basic math floating point operations.
Mathieeedoubbas.Library; Contains basic math functions for integers.
Mathtrans.Library; Contains higher level mathmatical functions.
Potgo.Library; Used for evaluating analog input to the Amiga.
Timer.Library; Contains routines for time critical programs.They can be used to program exact time intervals.
Translator.Library; Contains the single function "Translate",that translates normal text written phonetically for the narrator,the speech synthesisor.
You can open(load)all these libraries of course.You should remember that this takes time and memory.For this reason,you should always think about which functions you need and which libraries they are in. For example,lets say you want to write a program that does text input/output.You need the "Dos.Library",so it can be loaded. The "exec.library"is in charge of loading.This library contains the OpenLib function that can be called once you've passed the needed parameters.AssemPro Amiga includes all the libraries necessary for the Amiga,it also includes files that contain the offsets for the operating system calls.The macros contained in AssemPro ease assembly language programming considerably.To make the programs in this book useful to the largest audience the following examples are written for generic assemblers and do not include AssemPro's macros.We have used the AssemPro ILABEL and the macros INIT_AMIGA and EXIT_AMIGA so AssemPro owners can start the programs from the desktop.(If you are using a different assembler check your documentation for instructions on linking programs).
6.2.Calling Functions. ---------------------- Since this chapter is rather complex we'll first describe the fundamental routines necessary to use the Amiga's operating system after a description a complete program is listed.Every library begins in memory with a number of JMP commands.These JMPs branch to the routines that are in the library.To call a function,you need to find the beginning of this JMP table and call function x by going to the xth JMP command.Usually you use an offset to get to the right JMP command.Normally,you don't start at the beginning but at the end of the JMP table,so use negative offsets. It works out very easily.Now lets open the "dos.library"by using "exec.library's"base address.This address is $000004.To call a fuction from another library,you need to use another base address. Now you need the offset for the function that you want.You want the OpenLib function that has -408 as an offset.You'll find a list of function offsets in the appendix. You need a pointer to the name of the library you are loading for the OpenLib function(in this case "dos.library")and a long word in memory that you can use to store the base address of the DOS library.You get this back from the OpenLib function.You need to be sure to write the library name in lower case letters(dos.library), otherwise you can't open it.I entered a name in capitol letters once and spent a lot of time finding this error.
The routine looks like this:
;** Load the DOS library 'dos.library' (6.2A) ** Execbase = 4 ;base address of the EXEC library OpenLib = -408 ;offset for the OpenLib function
IoErr = -132 ;offset for IoErr information
init: move.l Execbase,a6 ;base address in A6 lea dosname,a1 ;address of library name moveq #0,d0 ;version number jsr OpenLib(a6) ;open DOS library move.l d0,dosbase ;save DOS base address beq error ;if zero,then error! ... ;your program goes here ... ;more program...
error: ;error move.l dosbase,a6 ;address of library name jsr IoErr(a6) ;call IoErr for error info move.l d0,d5 ... ;your error routine goes here rts
dosname: ;name of library to open dc.b 'dos.library',0,0 align ;SEKA uses-even
dosbase: ;storage for DOS base address blk.l 1
end
This is the way to load the DOS library so that you can use it.All library functions are called this way.Parameters are put in registers and passed to the function.When there is an error,when the function doesn't run correctly,a zero is usually put in data register D0. Once your program is done with its work,you need to close the libraries that are still open before you return to the CLI or Workbench.The CloseLib function (offset -414)takes care of this job.This function is in the EXEC library just like the OpenLib.The only parameter it needs is the base address of the library that is closed.To close "dos.library",do the following:
CloseLib = -414 ; (6.2B) ... move.l Execbase,a6 ;EXEC base address move.l dosbase,a1 ;DOS base address jsr CloseLib(a6) ;close library
6.3.Program Initialization. --------------------------- Before you can start a program,you need to initialize many things so that the program can run. Lets take an example program that does some text editing.A program like this must be able to store text,so it needs to be able to access memory.It also needs to be able to accept keyboard input and do screen output,so it needs an output window. To do this,you need to open one or more of the libraries that we talked about earlier.Lets assume that you've loaded the DOS library,so that you can do the next steps.
6.3.1.Reserve Memory. --------------------- There are several ways to get the operating system to assign you a chunk of memory.You need to use one of them,so that during multi- tasking,you don't have one program overwriting another programs memory area. Lets look at the function that is normally used.This function is in the resident EXEC library and has the name AllocMem (offset -$c6).It reserves a memory area,using the value in D0 as the length.The address that the memory area begins at is returned in the D0 data register.If it returns zero,the program could'nt give you that much memory. You can also use a mode word in D1 to determine whether the memory area that is reserved should be erased or not. The routine looks like this:
ExecBase = 4 ; (6.3.1A) AllocMem = -$c6 ... move.l #number,d0 ;number of bytes to reserve move #mode,a6 ;mode word move.l ExecBase,a6 ;DOS base address in A6 jsr AllocMem(a6) ;call function move.l d0,address ;save memory's start address beq error ;memory not reserved ...
The second way to reserve memory is to use the AllocAbs function (offset -$CC).This function in contrast to the AllocMem function reserves a particular memory area.The D0 register contains the number of bytes that should be reserved.Address register A1 contains the desired start address.This function returns a zero in D0 if the memory area can't be reserved.
ExecBase = 4 ; (6.3.1B) AllocAbs = -$cc ... move.l #number,d0 ;number of bytes to reserve lea address,a1 ;desired start address move.l execbase,a6 ;EXEC base address jsr AllocAbs(a6) ;reserve memory tst.l d0 ;everything ok? beq error ;no! ...
When the program has done its work and must return to the CLI or the Workbench,it needs to return the memory it as reserved to the system.The FreeMem function (offset -$D2) handles this. The function works like AllocAbs in that the number of bytes is put in D0 and the start address of the memory area is put in A1. If you try to free up a memory area that was'nt reserved,you'll usually crash the computor. The routine to free up a memory area looks like this:
ExexBase = 4 ; (6.3.1C) FreeMem = -$d2 ... move.l #number,d0 ;number of bytes released lea address,a1 ;start address from AllocAbs move.l ExecBase,a6 ;ExecBase address jsr FreeMem(a6) ;free up memory tst.l d0 ;everything ok? beq error ;no! ...
6.3.2.Opening a Simple Window. ------------------------------ The title of this chapter may sound a bit strange.However,the differences between the two different methods of opening a window are so great that they should be handled in seperate chapters. The method of opening a window presented here is very simple,but it doesn't allow you to work with all the gadgets.These gadgets include the close symbol in the upper left corner of a window and the size symbol in the lower left corner. If you open the window in the simple manner,almost all the gadgets are present.However,the close symbol is not.As a result,this method isn't appropriate for every application.Now lets look at the method. To open a window,use a function from the DOS library,so you need to open the library first (see the section "Load Library").This open function is an all purpose function that can be used for many things.For this reason,it makes good sense to put a "open" subroutine in your program.You can use it a lot.Lets do the basic steps:
;** Load the DOS Library 'dos.library' (6.3.2A) ** ExecBase = 4 ;base addres of the EXEC library OpenLib = -408 ;offset of OpenLib function Open = -30 ;Offset of the DOS function OPEN
init: move.l ExecBase,a6 ;base address in A6 lea dosname(pc),a1 ;address of library name move.q #0,d0 ;version number:unimportant jsr OpenLib(a6) ;call the function move.l d0,dosbase ;save DOS base address beq error ;if zero,then error! ... ;more of your program ... ;now open window,etc...
error: ... ;error occured ... ;your error routine
openfile: ;general open function move.l dosbase,a6 ;DOS base address in A6 jsr Open(a6) ;call OPEN function tst.l d0 ;test if ok rts ;done,evaluate test later
dosname: ;name of library to be opened dc.b 'dos.library',0,0 align ;even
dosbase: ;spot for DOS base address blk.l 1
You call the Openfile routine,because the label "Open"is already being used for the offset.This routine calls the Open function that is in the DOS library. This isn't everything.The function must be given some parameters so that it knows what to open.The parameters are sent in registers D1 and D2.D1 points to a definition block what specifies what should be opened.You need to have a filename ended with a null byte there.D1 must be passed as a long word like all addresses.D2 contains the mode that the function should run in.There is an old (1005) and a new (1006) mode.This number must be passed in D2's long word. Heres an overview of how windows are opened.Fortunately,AmigaDos allows you to use input and output channels in the same way.The standard channels are disk files,the console (keyboard and screen) the printer interface and the serial RS232 interface. The console input/output is what you'll work with now.When you specify the console as the filename of the channel to be opened,a window is opened automatically. The name must begin with CON:to do this.Its similar to DF0:for disk operations.A little more infotmation about the window is still needed. You need to specify the X and Y coordinates of the upper left and lower right corners of the window as well as the name that should appear in the title line of the window.A complete definition block for a window like this would appear like the following line:
consolname: dc.b 'CON:0/100/640/100/**Window**',0
To open this window,the line above needs to be inserted in the following program:
mode_old = 1005
lea consolname(pc),a1 ;consol definition move.l #mode_old,d0 ;mode bsr openfile ;console open beq error ;didn't work move.l d0,conhandle
rts ... conhandle: dc.l 1 ;space for handle
There are two points to clear up yet. You should use mode_old as the the mode when you open a window. Logically the window doesn't exist before opening so this seems wierd but it doesn't hurt anything. The parameter that returns from "openfile"in D0 is zero in the case of an error,in the case that opening didn't work.Otherwise the value is the identification number (handle number) of the opened channel.You need to store it away,because every function that wants to use this channel must give the handle number.In the example,you stored this number in the "conhandle"long word. As mentioned,the window you've opened doesn't have a close symbol but it can be made bigger and smaller and moved forward and back. The manipulations that are carried out using the mouse are completely taken care of by the Amiga (in contrast to the ATARI ST where the programmer has to take care of these things). An important function that uses the handle number is the one that closes the channel (in your case the window).This function is also in the DOS library and is called "Close".Its offset is -36 and it only needs one parameter;the handle number of the channel that is closed must be in the D1 register. After your work is done,you need to put the following lines in your program to close the window:
Close = -36 ; (6.3.2C) ... move.l conhandle,d1 ;handle number in D1 move.l dosbase,a6 ;DOS base address in A6 jsr Close(a6) ;close channel!
The window disappears!
Now for a few remarks about opening and closing the window in this way.If you open several windows in the same way,you'll get several windows and thus several handle numbers.In this way,you can put as many windows on the screen as you like.You can do your work with them and close them individually. Here is the complete program to open and close a simple window in AssemPro format (We have used the AssemPro ILABEL and the macros INIT_AMIGA and EXIT_AMIGA so AssemPro owners can start the program from desktop.If you are using a different assembler check your documentation for instructions on starting and exiting programs):
;***** 6.3.2 S.D *****
OpenLib =-30-378 closelib =-414 ;execbase =4 ;defined in AssemPro macros
*calls to Amiga DOS:
open =-30 close =-30-6 IoErr =-132 mode_old = 1005 alloc_abs =-$cc
ILABEL AssemPro:includes/Amiga.l ;AssemPro only
INIT_AMIGA ;AssemPro only
run: bsr init ;initialization bra test ;system-test
init: ;system initialization and open move.l execbase,a6 ;number of execute-library lea dosname(pc),a1 moveq #0,d0 jsr openlib(a6) ;open DOS-Library move.l d0,dosbase beq error
lea consolname(pc),a1 ;consol definition move.l #mode_old,d0 bsr openfile ;consol open beq error move.l d0,conhandle
rts
test:
bra qu ;quit and exit
error: move.l dosbase,a6 jsr IoErr(a6) move.l d0,d5
move.l #-1,d7 ;flag
qu: move.l conhandle,d1 ;window close move.l dosbase,a6 jsr close(a6) move.l dosbase,a1 ;DOS.Lib close move.l execbase,a6 jsr closelib(a6)
EXIT_AMIGA ;AssemPro only
openfile: ;open file move.l a1,d1 ;pointer to I/O-Definition-Text move.l d0,d2 move.l dosbase,a6 jsr open(a6) tst.l d0 rts
dosname: dc.b 'dos.library',0,0 Align.w dosbase: dc.l 0
consolname: dc.b 'CON:0/100/640/100/**CLI-Test**',0 Align.w
conhandle: dc.l 0
end
There is another way to open a window easily.Just use RAW:instead of CON:as the channel designator.All the other parameters and operations remain the same. If you try them both out,you won't see any differences between the two windows.They both look the same and can be worked with in the same way with the mouse.The difference comes when you input to the window.In the RAW:window,the cursor keys are ignored.In the CON: window and in CLI,they do work.
6.4.Input/Output. ----------------- Besides managing and making calculations with data,the most important work of a program is to input and output the data.There are many methods of data transfer in and out of the computor,for instance screen or printer output,keyboard input,using the serial or the parallel interface,tone or speech output and finally disk operations. You want to learn about all these methods of data input and output for programming and applications.We've written some programs as subroutines that should be useful for later programs.It makes good sense to make a library of these subroutines that can either be directly integrated in a new program or linked to a program.At the end of the sections there is a list of a complete program so you can see how the subroutines are used. To prepare for input/output,you need to have data to output and space to input data.To get this ready,you need a correct program beginning in which the EXEC and DOS libraries are opened and memory is reserved.After this,you begin most programs by outputing some text.The text can be a program title or the instruction to input data over the keyboard.Lets start looking at screen output.
6.4.1.Screen Output. -------------------- For a computor like the Amiga the first question is where should the screen output be sent?The answer is simple for many computors; they only have one screen,and output goes there.You need to specify which window to write to when you use the Amiga,however.
There are two possibilites:
1.Output to CLI
2.Output to another window
The first posibillity only exists if the program that makes the output was started from CLI.If not,you need to open your own custom window for your program.If so,you can use the window that was opened by the CLI for output. If you use the second method,you need to open a window.As you've already seen,there are three methods.For simple text and character output,the difference between the three sorts of windows isn't very great.Here you have a free hand in determining which sort of window to use.Lets open a CON:window and put its handle number in "conhandle". You've opened your window and want to output a title.You choose text to output and then put it in memory using a code segment like this:
title: dc.b "** Welcome to this Program! **" titleend: align ;even
The "align"(even) is a pseudo-op that should follow text when it is followed by either word data or program lines.It causes the assembler to insert a null byte if necessary to make the address even. To output this text you need another DOS function:Write.This has an offset of -48 and needs three parameters:
In D1 the handle of an opened output channel that should be written to (in your case,this is the handle number that you go back from the Open command when you opened your window.). In D2 the address of the text to be output (in the example,the address "title"). In D3 the number of characters to be output in bytes.
To find the number of bytes to output,you need to count the number of characters in your text.Use "titleend"to calculate this.Using this label,the assembler can calculate the length of your text for itself (after all,why should you count when you have a computor?) if you write:
move.l #titleend-title,d3
The advantage of specifying the length is that you can put control characters between the beginning and end of the text.In this way, you can execute certain functions using text output.You'll learn about the control characters in a bit.
Heres the routine:
Write = -48 ; (6.4.1A) ... ;open window ... move.l dosbase,a6 ;DOS base address move.l conhandle,d1 ;pass handle move.l #title,d2 ;text address move.l #titleend-title,d3 ;and length jsr Write(a6) ;call function ...
title: dc.b "** Welcome to this Program! **" titleend: align ;event
end
You'll certainly use this function a lot.You'll often want to output just one character though.To allow you to do this and similar text related tasks,there are four subroutines,each of which do a different sort of output:
Pmsg; Outputs the text from (D2) to the first null byte.
Pline; Is the same as the routine above except that the text is automatically followed by a CR,the cursor is positioned at the beginning of the next line.
Pchar; Outputs the character in D0
Pcrlf; Puts the cursor at the beginning of the next line.
Heres the subroutine package:
Write = -48 ; (6.4.1B) ...
pline: ;*output line and then a CR bsr pmsg ;output line
pcrlf: move #10,d0 ;line feed bsr pchar ;output move #13,d0 ;and CR
pchar: move.b d0,outline ;character in output buffer move.l #outline,d2 ;address of the character pmsg: ;*output line (D2) upto null move.l d2,a0 ;address in A0 clr d3 ;length = 0
ploop: tst.b (a0)+ ;null byte ? beq pmsg2 ;yes:length found addq.l #1,d3 ;else length + 1 bra ploop ;and continue looking
pmsg2: move.l dosbase,a6 ;DOS base address in A6 move.l conhandle,d1 ;our window handle jsr Write(a6) ;call write function rts ;done!
outline: dc.w 0 ;output buffer for 'pchar'
conhandle: dc.l 0 ;windows handle
Here is an example program to open and close a simple window and output a text message in AssemPro format (We have used the AssemPro macros INIT_AMIGA and EXIT_AMIGA so AssemPro owners can start the program from desktop.If you are using a different assembler check your documentation for instructions on starting and exiting programs.):
Here is the complete program in AssemPro format:
;***** 6.4.1C.asm S.D. *****
Openlib =-30-378 closelib =-414 ;execbase = 4 ;Defined in AssemPro ;Macros
* calls to Amiga Dos:
open =-30 close =-30-6 write =-48 IoErr =-132 mode_old = 1005 alloc_abs =-$cc
ILABEL AssemPro:include/Amiga.l ;AssemPro only
INIT_AMIGA ;AssemPro only
run: bsr init ;initialization bsr test ;system test nop bra qu ;quit and exit
test: move.l #title,d0 bsr pmsg bsr pcrlf bsr pcrlf
rts
init: ;system initialization and ;open move.l execbase,a6 ;number of execute-library lea dosname(pc),a1 moveq #0,d0 jsr openlib(a6) ;open DOS-library move.l d0,dosname beq error
lea consolname(pc),a1 ;console definition move.l #mode_old,d0 bsr openfile ;console open beq error move.l d0,conhandle
rts
pmsg: ;print message (D0) movem.l d0-d7/a0-a6,-(sp) move.l d0,a0 move.l a0,d2 clr.l d3
ploop: tst.b (a0)+ beq pmsg2 addq.l #1,d3 bra ploop ;length calculate
pmsg2: move.l conhandle,d1 move.l dosbase,a6 jsr write(a6) movem.l (sp)+,d0-d7/a0-a6 rts
pcrlf: move #10,d0 bsr pchar move #13,d0
pchar: ;output char in D0 movem.l d0-d7/a0-a6,-(sp) ;save all move.l conhandle,d1
pch1: lea outline,a1 move.b d0,(a1) move.l a1,d2 move.l #1,d3 ;1 letter move.l dosbase,a6 jsr write(a6) movem.l (sp)+,d0-d7/a0-a6 ;restore all
error: move.l dosbase,a6 jsr IoErr(a6) move.l d0,d5
move.l #-1,d7 ;flag
qu: move.l conhandle,d1 ;window close move.l dosbase,a6 jsr close(a6)
move.l dosbase,a1 ;DOS.Lib close move.l execbase,a6 jsr closelib(a6)
EXIT_AMIGA ;AssemPro only
openfile: ;open file move.l a1,d1 ;pointer to I/O-definition- ;text move.l d0,d2 move.l dosbase,a6 jsr open(a6) tst.l d0 rts
dosname: dc.b 'dos.library',0,0 align.w
dosbase: dc.l 0
consolname: dc.b 'CON:0/100/640/100/** CLI-Test **',0 align.w
conhandle: dc.l 0
title: dc.b '** Welcome to this Program! **'
titleend: align
outline: dc.w 0 ;output buffer for char
end
Using this program,you can very easily put whatever you want in the CON:window.These functions also work in RAW:window.You should rename "conhandle"as "rawhandle",so that you don't get things mixed up later. Lets stay with the CON:window.As mentioned earlier,you can output special characters that execute functions or change parameters for output.These characters are called control characters. You've already learned about one of these control characters,Line Feed ($A).This character isn't just output;instead,it calls a function that moves the cursor into the next line and moves the screen up.This is very useful,but there are much more interesting control characters. Here's a list of control characters that execute functions.These characters are given in hex.
Control Sequence;
Sequence Function ------------------------------------------------------------------ 08 Backspace 0A Line Feed,Cursor down 0B Move Cursor up a line 0C Clear screen 0D Carrige return,cursor in the first column 0E Turn on normal characters (Cancel Of Effects) 0F Turn on special characters 1B Escape
The following sequences begin with $9B,the CSI (Control Sequence Introducer).The characters that follow execute a function.The values in square brackets can be left off.The n's you see represent one or more digit decimal numbers given using ASCII characters.The value that is used when n is left off,is given in the parenthesis that follow n in the description of the function in the table.
Control Sequence Introducer;
Sequence Function ------------------------------------------------------------------ 9B[n]40 Insert n blanks 9B[n]41 Move cursor n (1) lines up 9B[n]42 Move cursor n (1) lines down 9B[n]43 Move cursor n (1) characters to the right 9B[n]44 Move cursor n (1) characters to the left 9B[n]45 Move cursor down n (1) lines into column 1 9B[n]46 Move cursor up n (1) lines and into column 1 9B[n][3B n]48 Cursor in line;Set column 9B 4A Erase screen from cursor 9B 4B Erase line from the cursor 9B 4C Insert line 9B 4D Delete line 9B[n]50 Delete n characters starting at cursor 9B[n]53 Move up n lines 9B[n]54 Move down n lines 9B 32 30 68 Line feed => Line feed + return 9B 32 30 6C Line feed => just Line feed 9B 6E Sends the cursor position!A string of the following form is returned: 9B (line) 3B (column) 52 9B(style);(foreground colour);(Background Colour)6D The three parameters are decimal numbers in ASCII format.They mean: Style: 0 = normal 1 = bold 3 = italic 4 = underline 7 = inverse Foreground colour: 30-37 Colour 0-7 for Text Background colour: 40-47 Colour 0-7 for background 9B(length)74 sets the maximum number of lines to be displayed 9B(width)75 sets the maximum line length 9B(distance)78 defines the distance in pixels from the left border of the window to the place where output should begin 9B(distance)79 defines the distance in pixels from the upper border of the window to the place where output should begin The last four functions yield the normal values if you leave off the parameters.
9B 30 20 70 Make cursor invisible 9B 20 70 Make cursor visible 9B 71 Sends window construction.A string of the following form is returned: 9B 31 3B 31 3B (lines) 3B (columns) 73
To see how the control characters work,have "pmsg"output this text to your window:
mytext: dc.b $9b,"4;31;40m" ; (6.3.2D) dc.b "underline" dc.b $9b,"3;33;40m",$9b,"5;20H" dc.b "** Hello World! **",0
The parameters for the control sequence are put in quotation marks so they are treated as an ASCII string.Now you see,just how easy it is to do text output! Here is the complete program to open and output the text and control codes to your window in AssemPro format (We have used the AssemPro macros INIT_AMIGA and EXIT_AMIGA so AssemPro owners can start the programs from desktop.If you are using a different assembler check your documentation for instructions on starting and exiting programs):
; ***** 6.4.1D.ASM S.D. *****
openlib =-30-378 closelib =-414 ;execbase = 4 ;defined in AssemPro macros
* calls to Amiga Dos:
open =-30 close =-30-6 write =-48 IoErr =-132 mode_old = 1005 alloc_abs =-$cc
ILABEL AssemPro:includes/Amiga.l ;AssemPro only
INIT_AMIGA ;AssemPro only
run: bsr init ;initialization bsr test ;system test nop bra qu ;quit and exit
test: move.l #mytext,d0 bsr pmsg bsr pcrlf bsr pcrlf
rts
init: ;system initialization and open move.l execbase,a6 ;number of execute-library lea dosname(pc),a1 moveq #0,d0 jsr openlib(a6) ;open DOS-Library move.l d0,dosbase beq error
lea consolname(pc),a1 ;console definition move.l #mode_old,d0 bsr openfile ;console open beq error move.l d0,conhandle
rts
pmsg: ;print message (D0) movem.l d0-d7/a0-a6,-(sp) move.l d0,a0 move.l a0,d2 clr.l d3
ploop: tst.b (a0)+ beq pmsg2 addq.l #1,d3 bra ploop
pmsg2: move.l conhandle,d1 move.l dosbase,a6 jsr write(a6) movem.l (sp)+,d0-d7/a0-a6 rts
pcrlf: move #10,d0 bsr pchar move #13,d0
pchar: ;output char in D0 movem.l d0-d7/a0-a6,-(sp) ;save all move.l conhandle,d1
pch1: lea outline,a1 move.b d0,(a1) move.l a1,d2 move.l #1,d3 ;one letter move.l dosbase,a6 jsr write(a6) movem.l (sp)+,d0-d7/a0-a6 ;restore all rts
error: move.l dosbase,a6 jsr IoErr(a6) move.l d0,d5
move.l #-1,d7 ;flag
qu: move.l conhandle,d1 ;window close move.l dosbase,a6 jsr close(a6)
move.l dosbase,a1 ;DOS.Lib close move.l execbase,a6 jsr closelib(a6)
EXIT_AMIGA ;AssemPro only
openfile: ;open file move.l a1,d1 ;pointer to I/O-definition- ;text move.l d0,d2 move.l dosbase,a6 jsr open(a6) tst.l d0 rts
dosname: dc.b 'dos.library',0,0 align.w
dosbase: dc.l 0
consolname: dc.b 'CON:0/100/640/100/ ** CLI-Test **',0 align.w
conhandle: dc.l 0
mytext: dc.b $9b,'4;31;40m' dc.b 'underline' dc.b $9b,'3;33;40m',$9b,'5;20H' dc.b '** Hello World !! **',0
align
outline: dc.w 0 ;output buffer for pchar
end
Now that you've done text and character output,its time to move on to text input.
6.4.2.Keyboard Input. --------------------- You can read keyboard input very easily.You just need to open the I/O channel of the CON:window and read from it.You need the read function from the DOS library to do this.Its offset is -42. The function has three parameters just like the WRITE function.
In D1 the handle number that you get from the WRITE function. In D2 the address that the data read in is to start. In D3 the number of bytes to read.
Here is a subroutine that reads the number of characters from the keyboard that it finds in D3.It puts them in a buffer.
read = -42 ; (6.4.2A) ...
getchr: ;* Get (D3) characters from the ;keyboard move.l #inbuff,d2 ;address of buffer in D2 move.l dosbase,a6 ;DOS base address in A6 move.l conhandle,d1 ;our window handle jsr read(a6) ;call read function rts ;done!
inbuff: blk.b 80,0 ;buffer for keyboard input
This routine returns to the main program when <Return> is entered. If more than D3 characters are entered,"inbuff"only gets the first characters.The routine gets the remaining characters when called a second time. This sort of input is fairly easy.You can backspace,because only the characters that should be there are put in the memory block starting at "inbuff".The number of characters moved into "inbuff" is put in D0. Try the program out as follows:
After opening the CON:window,put the following lines in the main program:
move #80,d3 ;read 80 characters (6.4.2B) bsr readchr ;get line from keyboard lea inline,a0 ;address of line in A0 clr.b 0(a0,d0) ;null byte on the end bsr pmsg ;output line again bp:
After this comes the code segment that closes the window again. After loading the program into the AssemPro debugger,make "bp"a breakpoint and start the program.(SEKA users start the program with "g run"and enter "bp"as the breakpoint).The program quits at the breakpoint and you can take a look at the results on the screen.Then you can continue the program (SEKA with "j bp") and let the window close. After starting the program and opening the window,the cursor appears in the upper left corner of the window.Enter some text and press <Return>.The string that you just entered is output again on the screen. You use the "pmsg"routine from the previous chapter to do this output.This routine needs a null byte at the end of the text to be output.You put a null byte there by putting the address of the input buffer in A0 and then erasing the byte at A0+D0 using the CLR.B command.Since D0 contains the number of characters that were entered,this byte is the first unused byte. Since you're in the debugger you can redisplay the disassembled output when the program ends to see what "getchr"put in "inbuff" (SEKA owners can use "q inbuff"when the program ends to see what "getchr"put there.)You'll find the characters that you typed plus a closing $A.The $A stands for the <Return> key and its counted too,so if you entered a 12 and then hit <Return>,for example,D0 will contain a 3. Try this again with a RAW:window.Change the window definition from CON: to RAW:and reassemble the program.You'll notice the diference right away.After you've entered one character,a return is executed D0 always as one bit in it. The advantage of this form of input is that cursor and function keys can be recognized.Using your own routine,you can repeatedly accept input of characters using "getchr"and then work with the special characters. Theres another form of keyboard input:checking for a single key. This is important when a program is about to execute an important function and the user must say he wants it executed by entering "Y"for yes.This can be treated as normal input,but in some cases, there is a better method. There is a function in the DOS library that waits a certain specified length of time for a key to be pressed,and returns a zero (FALSE) if no key was hit in this time period.It returns a -1 ($FFFFFFFF = TRUE) if one was.To find out which key it takes another function.The WaitForChar function,is only good for tasks like waiting for the user to let the program know that it can continue scrolling text.
The function needs two parameters:
In D1 the handle number of the window or file from which the character should be read.It can also wait for a character from an interface. In D2 you pass the length of time in microseconds that you should wait for a key stroke.
To wait one second for one key to be hit,you can use the following routine:
WaitForCh=-30-174 ; (6.4.2C) ...
scankey: ;* Wait for a key stroke move.l conhandle,d1 ;in our window move.l #1000000,d2 ;waiting time 1 second move.l dosbase,a6 ;DOS base address jsr waitforch(a6) ;wait... tst.l d0 ;test result rts
The TST command at the end of the program allows the calling routine to use a BEQ or BNE command to evaluate the results of the routine-BEQ branches if no key was hit.BNE doesn't. Heres an example program in AssemPro format covering what you have learned so far.Opening and closing a window,displaying text in the window and inputting text:
;***** 6.4.2A.ASM S.D *****
openlib =-30-378 closelib =-414 ;execbase =4 ;defined in AssemPro ;Macros
* call to Amiga.Dos:
open =-30 close =-30-6 read =-42 write =-48 IoErr =-132 mode_old =1005 alloc_abs =-$cc
ILABEL AssemPro:include/Amiga.l ;AssemPro only
INIT_AMIGA ;AssemPro only
run: bsr init ;initialization bsr test ;system test nop bra qu ;quit and exit
test: move.l #mytext,d0 bsr pmsg bsr pcrlf bsr pcrlf move.l #80,d3 ;80 characters to read in (D3) bsr getchr ;get character bsr pmsg ;output line
rts
init: ;system initialization and open move.l execbase,a6 ;number of execute-library lea dosname(pc),a1 moveq #0,d0 jsr openlib ;open DOS-Library move.l d0,dosbase beq error lea consolname(pc),a1 ;console definition move.l #mode_old,d0 bsr openfile ;console open beq error move.l d0,conhandle
rts
pmsg: ;print message (D0) movem.l d0-d7/a0-a6,-(sp) move.l d0,a0 move.l a0,d2 clr.l d3
ploop: tst.b (a0)+ beq pmsg2 addq.l #1,d3 bra ploop ;check length
pmsg2: move.l conhandle,d1 move.l dosbase,a6 jsr write(a6) movem.l (sp)+,d0-d7/a0-a6 rts
pcrlf: move #10,d0 bsr pchar move #13,d0
pchar: ;character in D0 output movem.l d0-d7/a0-a6,-(sp) ;save all move.l conhandle,d1
pch1: lea outline,a1 move.b d0,(a1) move.l a1,d2 move.l #1,d3 ;1 letter move.l dosbase,a6 jsr write(a6) movem.l (sp)+,d0-d7/a0-a6 ;restore all rts
getchr: ;get character for keyboard move.l #1,d3 ;1 character move.l conhandle,d1 lea inbuff,a1 ;buffer address move.l a1,d2 move.l dosbase,a6 jsr read(a6) clr.l d0 move.b inbuff,d0 rts
error: move.l dosbase,a6 jsr IoErr(a6) move.l d0,d5
move.l #-1,d7 ;flag
qu: move.l conhandle,d1 ;window close move.l dosbase,a6 jsr close(a6)
move.l dosbase,a1 ;DOS.Lib close move.l execbase,a6 jsr ;close lib (A6)
EXIT_AMIGA ;AssemPro only
openfile: ;open file move.l a1,d1 ;pointer to I/O-Definition- ;Text move.l d0,d2 move.l dosbase,a6 jsr open(a6) tst.l d0 rts
dosname: dc.b 'dos.library',0,0 align.w
dosbase: dc.l 0
consolname: dc.b 'CON:0/100/640/100/** CLI-TEST **',0 align.w
conhandle: dc.l 0
mytext: dc.b '** Hello World !! **',0
align
outline: dc.w 0 ;output buffer for pchar inbuff: blk.b 8 ;input buffer
end
6.4.3.Printer Control. ---------------------- Now that you've looked at console I/O,lets look at outputting data from the computor.The first device that we'll discuss is the printer. Its very easy to use the printer.You just need to open another channel.It goes just the way you learned it with CON: and RAW: windows;the only difference is you enter PRT:instead. You open this channel using the same lines that you used above for the window except that the pointer is to the channel name PRT:in D1.You pass the mode "new"(1006) in D2 in the "do_open"routine as well.Save the handle number that comes back at a label called "prthandle". Now you can use the same output routines that you used with the windows to send text to the printer.You need to put "prthandle" instead of "conhandle"in the line with the "move.l conhandle,d1" command. Actually it would be better to eliminate this line from the routine totally.Then you can use the same routine for window and printer output.The calling procedure would then need to put "conhandle"in D1 for window output.It would put "prthandle" in D1 for printer output.This is a very flexible output routine that can be used for window and printer output now.You can't accept input from the printer,because the printer doesn't send data.It just accepts it and prints it.
6.4.4.Serial I/O. ----------------- Its just as easy to use the serial interface as the printer.Just enter SER:as the filename.Now you can use the DOS functions READ and WRITE just as before to do I/O channels you've just opened. You can set the parameters for the interface (like Hand shake and Transfer rate) with the Preferences program.
6.4.5.Speech Output. -------------------- The Amiga has a speech synthesizer built in.This isn't quite as easy to program as the I/O devices discussed earlier,however.You use the "narrator.device"to do this. This device requires several program steps to install it and then causes it to speak.You need to open the device,start the I/O,etc.. Lets look at how to translate the text into the proper form and then output the text. First we need to do some initialization.Lets define the constants now.Some of them are new.
;***** Narrator Basic Functions 3/87 S.D ***** (6.4.5A)
openlib =-408 closelib =-414 execbase = 4
open =-30 ;open file close =-36 ;close file mode_old = 1005 ;old mode opendevice =-444 ;open device closedev =-450 ;close device
sendIo =-462 ;start I/O abortIO =-480 ;abort I/O
translate =-30 ;translate text
;The initialization routine follows: init: ;initialize and open system
;* open DOS library *
move.l execbase,a6 ;pointer to execbase lea dosname,a1 ;pointer to DOS name moveq #0,d0 ;version unimportant jsr openlib(a6) ;open DOS library move.l d0,dosbase ;save handle beq error ;error handle
;* Open translator.library *
lea transname,a1 ;pointer to translator name clr.l d0 jsr openlib(a6) ;open translator move.l d0,transbase ;save handle beq error ;error handling
;* Set up I/O area for Narrator *
lea talkio,a1 ;pointer to I/O area in A1 move.l #nwrrep,14(a1) ;enter port address move.l #amaps,48+8(a1) ;pointer to audio mask move #4,48+12(a1) ;number of the mask move.l #512,36(a1) ;length of the output area move #3,28(a1) ;command:write move.l #outtext,40(a1) ;address of output area
;* Open Narrator device *
clr.l d0 ;number 0 clr.l d1 ;no flags lea nardevice,a0 ;pointer to device name jsr opendevice(a6) ;open narrator.device tst.l d0 :error? bne error ;Yes!
;* Open Window *
move.l #consolname,d1 ;console definition move.l #mode_old,d2 ;old mode move.l dosbase,a6 ;DOS base address jsr open(a6) ;open window tst.l d0 ;error? beq error ;Yes! move.l d0,conhandle ;else save handle
After you've done this initialization,you can have the computor save the text you have prepared for it.To see what the Amiga is saying,use the "pmsg"function to have the text written to the window:
move.l #intext,d2 ;text for the Amiga to say bsr pmsg ;output in window also
sayit: ;have the text said
;*Translate the text into a form that the computor can use *
lea intext,a0 ;address of the text move.l #outtext-intext,d0 ;length of the text lea outtext,a1 ;address of output area move.l #512,d1 ;length of output area move.l tranbase,a6 ;translator base address jsr translate(a6) ;translate text
;* Speech output *
lea talkio,a1 ;address of I/O structure move.l #512,36(a1) ;length of output area move.l execbase,a6 ;EXEC base address jsr sendIO(a6) ;start I/O (speech output)
Once the program ends,the I/O stops as well,so you need to put in something that keeps the program going longer.You'll use the "getchr"function that you programmed earlier to take care of this:
bsr getchr ;wait for keyboard input
The computor waits until the <Return> key is pressed.Now you can listen to what the Amiga as to say.Once the <Return> key is pressed,the program stops.
qu: ; (6.4.5C) move.l execbase,a6 ;EXEC base address lea talkio,a1 ;pointer to I/O area jsr abortio(a6) ;stop the I/O
move.l conhandle,d1 move.l dosbase,a6 jsr close(a6) ;close window move.l dosbase,d1 move.l execbase,a6 jsr closelib(a6) ;close DOS library
lea talkio,a1 jsr closedev(a6) ;close narrator.device
move.l tranbase,a1 jsr closelib(a6) ;close translator library
rts ;* end of program
Now comes the data that you need for the program above:
mytext: dc.b 'This is a test text !',10,13,10,13,0,0 dosmame: dc.b 'dos.library',0,0 transname: dc.b "translator.library",0 consolname: dc.b 'RAW:0/100/640/100/** Test window',0 nardevice dc.b 'narrator.device',0 align dosbase: dc.l 0 tranbase dc.l 0 amaps: dc.b 3,5,10,12 align conhandle: dc.l 0 talkio: blk.l 20,0 nwrrep: blk.l 8,0 intext: dc.b 'hello,i am the amiga talking to you',0 align outtext: blk.b 512,0
This is quite a bit of work,but its worth it because it opens so many possibilities for you.There are a lot of variations possible if you modify parameters.These parameters are entries in the I/O area starting at the "talkio"label.The area is built as follows:
Offset Length Meaning ---------------------------------------------------------------- ** Port Data ** 0 L Pointer to next block 4 L Pointer to last block 8 B I/O type 9 B Priority 10 L Pointer to I/O name 14 L Pointer to port 18 W Length ** I/O Data ** 20 L Pointer to device 24 L Pointer to device unit 28 W Command word 30 B I/O flags 31 B I/O status 32 L I/O pointer 36 L I/O length 40 L Pointer to Data 44 L I/O offset ** Narrator data items ** 48 W Speech speed 50 W Highness of voice 52 W Speech mode 54 W Sex (male/female voice) 56 L Pointer to audio mask 60 W Number of mask 62 W Volume 64 W Read in rate 66 B Flag for producing graphics (0=off) 67 B Actual mask (internal use) 68 B Channel used (internal use)
We would'nt recommend experimenting with the data in the first two blocks.If you do,you can easily cause a system crash.You can use the last entries of the structure to produce some interesting effects though. Heres an overview of the parameters you can use to vary the speech output.The value in parenthesis is the standard value,the value set when narrator.device is opened.
Speech speed (150); You can use this to set the speed of speech.The pitch of the voice is not affected by this value.
Pitch of voice (110); You can choose a value between 65 and 320 for the pitch (from Goofy to Mickey Mouse).
Speech mode (0); The zero gives half-way naturel speech.A one lets the Amiga speak in monotone like a robot.
Sex (0); A zero means masculine and a one means feminine (more or less..)
Volume (64); The volume can range from 0 to 64.The standard value is the loudest possible.
Read in rate (22200); By lowering this value,the voice is lowered.If you change this very much,you'll get some wierd voices!
You can experiment a bit until you find a interesting voice.Have fun! Here is a complete talking program in AssemPro format:
;***** Speech output S.D. *****
openlib =-30-378 closelib =-414 ;execbase =4 ;defined by AssemPro
* calls to Amiga Dos:
open =-30 close =-30-6 opendevice =-444 closedev =-450 addport =-354 remport =-360 ;DoIo =-456 sendIo =-462 abortIo =-480 read =-30-12 write =-30-18 ;myinput =-30-24 ;output =-30-30 ;currdir =-30-96 ;exit =-30-114 waitforch =-30-174 findtask =-294 translate =-30 mode_old = 1005 ;mode_new = 1006 ;alloc_abs =-$cc ;free_mem =-$d2
;!!!when>500KB !!! or place in chip memory ;org $40000 ;load $40000 ;!!!!!!!!!!!!!!!!!!!!!!!
ILABEL AssemPro:includes/Amiga.l ;AssemPro only
INIT_AMIGA ;AssemPro only
run: bsr init ;initialization bra test ;system-test
init: ;system initialization and ;open move.l execbase,a6 ;pointer to exec library lea dosname(pc),a1 ;pointer to dos name moveq #0,d0 ;version:not important jsr openlib(a6) ;open DOS-Library move.l d0,dosbase ;save handle beq error ;error routine
;* ;open translator library move.l execbase,a6 ;pointer to exec library lea transname,a1 ;pointer to translator library clr.l d0 jsr openlib(a6) ;open translator move.l d0,tranbase ;save handle beq error ;error routine
;* ;set up sub.l a1,a1 move.l execbase,a6 jsr findtask(a6) ;find task move.l d0,nwrrep+2
lea nwrrep,a1 jsr addport(a6) ;add port
;* ;open narrator device lea talkio,a1 ;pointer to I/O area in A1 move.l #nwrrep,14(a1) ;enter port address clr.l d0 ;number 0 clr.l d1 ;no flags lea nardevice,a0 ;pointer to device name jsr opendevice(a6) ;open narrator.device tst.l d0 ;error? bne error ;Yes!
;* ;set up I/O for narrator ;device
bp: lea talkio,a1 ;pointer to I/O in A1 move.l #nwrrep,14(a1) ;enter port address move.l #amaps,48+8(a1) ;pointer to audio mask move #4,48+12(a1) ;size of mask
lea consolname(pc),a1 ;console-definition move.l #mode_old,d0 bsr openfile ;console open beq error move.l d0,conhandle
rts
test: move.l #mytext,d0 bsr pmsg ;test-text output bsr sayit ;say text
bsr readin ;input move #10,d0 bsr pchar ;LF output move.l #inline+2,d0 bsr pmsg ;and again bsr pcrlf bra qu
error: move.l #-1,d7 ;flag
qu: move.l execbase,a6 lea talkio,a1 jsr abortio(a6) move.l conhandle,d1 ;window close move.l dosbase,a6 jsr close(a6)
move.l dosbase,a1 ;DOS.Lib close move.l execbase,a6 jsr closelib(a6)
lea nwrrep,a1 jsr remport(a6) ;remove port lea talkio,a1 jsr closedev(a6) ;close narrator device move.l tranbase,a1 jsr closelib(a6) ;close translator library
EXIT_AMIGA ;AssemPro only
openfile: ;open file move.l a1,d1 ;pointer to I/O definition- ;text move.l d0,d2 move.l dosbase,a6 jsr open(a6) tst.l d0 rts
pmsg: ;print message (D0) movem.l d0-d7/a0-a6,-(sp) move.l d0,a0 move.l a0,d2 clr.l d3
mess1: tst.b (a0)+ beq mess2 addq.l #1,d3 bra mess1 ;length calculate
mess2: move.l conhandle,d1 move.l dosbase,a6 jsr write(a6) movem.l (sp)+,d0-d7/a0-a6 rts
pcrlf: move #10,d0 bsr pchar move #13,d0
pchar: ;output characters in D0 movem.l d0-d7/a0-a6,-(sp) ;save all move.l conhandle,d1
pch1: lea chbuff,a1 move.b d0,(a1) move.l a1,d2 move.l #1,d3 ;1 letter move.l dosbase,a6 jsr write(a6) movem.l (sp)+,d0-d7/a0-a6 ;restore all rts
scankey: ;test key move.l conhandle,d1 move.l #500,d2 ;wait value move.l dosbase,a6 jsr waitforch(a6) tst.l d0 rts
readin: ;input from keyboard movem.l d0-d7/a0-a6,-(sp) ;save registers lea inline+2,a2 ;pointer to input buffer clr.l (a2)
inplop: bsr getchr cmp.b #8,d0 beq backspace cmp.b #127,d0 ;delete? beq backspace bsr pchar ;character output cmp.b #13,d0 beq inputx move.b d0,(a2)+ bra inplop
inputx: clr.b (a2)+ sub.l #inline,a2 move a2,inline ;length in lines+1 movem.l (sp)+,d0-d7/a0-a6 ;registers rts
backspace: cmp.l #inline,a2 ;at the beginning? beq inplop ;yes move.b #8,d0 bsr pchar ;backspace move #32,d0 bsr pchar ;blank move #8,d0 bsr pchar ;backspace clr.b (a2) subq.l #1,a2 bra inplop
getchr: ;get one character from ;keyboard move.l #1,d3 ;one character move.l conhandle,d1 lea inbuff,a1 ;buffer address move.l a1,d2 move.l dosbase,a6 jsr read(a6) clr.l d0 move.b inbuff,d0 rts
sayit: lea intext,a0 move.l #outtext-intext,d0 lea outtext,a1 move.l #512,d1 move.l tranbase,a6 jsr translate(a6)
p: lea talkio,a1 move #3,28(a1) ;?? move.l #512,36(a1) move.l #outtext,40(a1) move.l execbase,a6 jsr sendio(a6) rts
mytext: dc.b 'This is our Test-Text !',10,13,10,13,0,0
dosname: dc.b 'dos.library',0,0 transname: dc.b "translator.library",0 align.w
dosbase: dc.l 0 tranbase: dc.l 0
consolname: dc.b 'CON:0/100/640/100/* Speech-Test S.D.* ',0
nardevice: dc.b 'narrator.device',0
amaps: dc.b 3,5,10,12,0,0 align.w
conhandle: dc.l 0
inbuff: blk.b 8
inline: blk.b 180,0 chbuff: blk.b 82,0
narread: blk.l 20,0 talkio: blk.l 20,0
nwrrep: blk.l 8,0
intext: dc.b 'hello,i am the amiga computor',0 align.w
outtext: blk.l 128,0
end
6.5.Disk Operations. -------------------- The most important peripheral device for a computor like the Amiga is the disk drive.You use it to save data,so that you don't lose it when you turn off the computor.We'll look at saving and retrieving data in this chapter. Lets first look at the simple disk operations that are used for data management.To gain access to a file,you must open it first. This is done using the OPEN function from the DOS library,a function that you're already familiar with.I'll assume in the following examples,that you've already opened the DOS library.
6.5.1.Open Files. ----------------- The open function needs a parameter for the mode.The mode has a particular meaning.If the file is opened for reading,it must already exist.The mode for the OPEN function must be "old"(1005) in this case. If you want to produce a file,you must open it first.Since it does not exist,you use the "new"(1006) mode.If a file is opened for writing using this mode even though a file with this name already exists,the old file with this name is erased and replaced.To avoid loss of data,you should check if a file by that name already exists and then output an error message if it does. You're going to start with a subroutine that opens a file.Lets assume that the filename starts at the label "filename",and that it is closed with a null byte.You just need to pass the mode in register D2. The routine puts the file handle number in "filehd"and returns to the main program.Since the operation with the handle is the last one performed by the subroutine,the status of the operation can be evaluated once the return has been executed.If the operation went smoothly and the file is opened,the handle number has a non-zero value.If it is zero and "bsr openfile"is followed by "beq error", you can branch to an error handling routine when problems occur.
Here is a subroutine for opening and closing a file:
open =-30 ; (6.5.1A) close =-36 mode_old = 1005 mode_new = 1006 ... openfile: ;*open file,mode in D0 move.l dosbase,a6 ;DOS base address in A6 move.l #filename,d1 ;pointer to filename jsr open(a6) ;open file move.l d0,filehd ;save handle rts
closefile: ;*close file move.l dosbase,a6 ;DOS base address in A6 move.l filehd,d1 ;file handle in D1 jsr close(a6) ;close file rts
filehd: dc.l 0 ;storage for file handle filename: dc.b "filename",0 ;file to be opened align ;even
To use these subroutines,you must look at how you can load and save data. 6.5.2.Reading and Writing Data. ------------------------------- Lets write a new file.To start,write the following lines:
move.l #mode_new,d2 ;open new file (6.5.2A) bsr openfile ;open file beq error ;did'nt work!
For the filename,write a name like "Testfile"in the line labelled "filename".After calling the "openfile"routine,a file with this name is created on the disk.If one existed already,it is erased.
Lets assume you want to write a short text file.For the example lets use:
text: dc.b "This is a test text for the Testfile",0 textend:
The "textend"label is used so that you can calculate the number of data bytes by subtracting "text". You want to write this text in the file.Use the WRITE function which needs three parameters:
In D1 the file handle that you got back from the OPEN function. In D2 a pointer to the data that should be written. In D3 the number of bytes to be written.
For the example,you'll need another segment of code to put the pointer to the data in D2 and the number of bytes in D3:
write =-48 ; (6.5.2B) ... writedata: ;*write data in the file move.l dosbase,a6 ;DOS base address move.l filehd,d1 ;file handle in D1 jsr write(a6) ;write data rts
After opening the file,you can call the subroutine from the main program with the following lines:
move.l #text,d2 ;pointer to data move.l #textend-text,d3 ;number of bytes bsr writedata ;write data in the file
Then close the file with:
bsr closefile ;close file bra end ;end program
After running the program,look at the directory of the diskette, you should find the file "testfile".It is just as long as your text.You want to read this file in,to make sure it contains the right data. You need the DOS function READ,which needs the same parameters as the WRITE function.You can use parameters for the number of bytes to read just part of the file.If you give a larger number than the file contains,the whole file is loaded.You'll find the number of bytes read in D0. Lets set up a field that as enough space for the data you want to read.You can do this with the following line:
field: blk.b 100 ;reserve 100 bytes
For the example data,this is plenty.If you want to load another file,you may need to reserve more space. Now lets write a subroutine to read the data.You always want to load whole files.You just need to pass the address of the buffer so the data is loaded into the subroutine.In the example,its the address "field". Heres the subroutine that reads the entire opened file into the memory area pointed to by D2:
read = -42 ; (6.5.2C) ... readdata: ;*read file move.l dosbase,a6 ;DOS base address in A6 move.l filehd,d1 ;file handle in D1 move.l #$ffffff,d3 ;read an arbitrary number of bytes jsr read(a6) ;read data rts
To use this routine to load the file into the buffer "field",use the following main program:
move,l #mode_old,d2 ;old file bsr openfile ;open file beq error ;did'nt work! move.l #field,d2 ;pointer to data buffer bsr readdata ;read file move.l d0,d6 ;save number of bytes in D6 bsr closefile ;close file bra end ;program end
After assembling and starting this program,you can use the debugger to look at the data buffer that you filled with data from the file.In D6,you'll find the number of bytes that were read from the file.
6.5.3.Erase Files. ------------------ Once you've experimented enough with the program above,you'll certainly want to erase the "Testfile"file.The DELETEFILE function in the DOS library has an offset of -72.It only needs 1 parameter. The parameter is passed in D1.The parameter is a pointer to the filename.The name must be closed with a null byte.
To erase "Testfile",use the following lines:
deletefile =-72 ; (6.5.3) ... move.l dosbase,a6 ;DOS base address in A6 move.l #filename,d1 ;pointer to filename in D1 jsr deletefile(a6) ;erase file
The file is deleted.You can't save the file with normal methods if you accidently erase it!You can use a trick that saves the data. We'll take a look at this trick later.Its used in lots of programs
6.5.4.Rename Files. ------------------- When a text editing program writes a text that as be altered back to the disk,the old file usually isn't erased.Often the old file is renamed.For example,it might get the name "Backup".The new file is written to disk with the old name. The function in the DOS library that allows you to change the names of programs is called RENAME and has -78 as an offset.You need to pass two parameters-D1 as a pointer to the old name and D2 as a pointer to the new name of the file.
To rename "Testfile"as "Backup"(before you erase it),use the following lines:
rename =-78 ... move.l dosbase,a6 ;DOS base address in A6 move.l #oldname,d1 ;pointer to old name in D1 move.l #newname,d2 ;pointer to new name in D2 jsr rename(a6) ;rename file ... oldname: dc.b "testfile",0 newname: dc.b "backup",0
6.5.5.CLI Directory. -------------------- Lets pretend you've programmed a text editor and started it.Now you want to load a text from disk and edit it-but whats the name of that file? You need a function to read and display the directory of the disk. There are several ways to do this.First lets use the easiest method.It doesn't require much programming and can be quite useful. The trick is to call the Dir or List programs that are in the C directory.You'll use the CLI commands.The DOS library contains a command called "Execute"with offset -222 that allows you to execute CLI commands. The function needs three parameters:
In D1 a pointer to a string closed with a zero that contains the name of the command to be executed.This string must contain the same command that you would give in the CLI.It can be a null pointer as well. In D2 the input file is determined.Normally theres a zero here. If however,you give the file handle of a text file,though, this file is read and interpreted as a command sequence.If you define a window as the input medium,you've programmed a new CLI window! In D3 the output file is determined.If there a zero here,the output of the commands (for example,DIR output) is sent to the standard CLI window.
To try this out,insert this subroutine in a program that has already opened the DOS library and a window.
execute = -222 ; (6.5.5) ... dir: move.l dosbase,a6 ;DOS base address in A6 move.l #command,d1 ;pointer to command line clr.l d2 ;no input (CLI window) move.l conhandle,d3 ;output in our window jsr execute(a6) ;execute command rts
command: dc.b "dir",0
This program works with the List command as well.The disadvantage of this method is that the disk that the Workbench is loaded from must be in the drive or the system requests you to put it in.The Dir command is just a program,and the Amiga must load it before it can run. The disadvantage isn't too great.The program is short,and it allows you to use any CLI command in a program. Here is the complete program in AssemPro format that calls the dir program:
;***** 6.5.5A DIR.ASM S.D.***** openlib =-408 closelib =-414 ;execbase = 4 ;defined in AssemPro ;macros *calls to Amiga Dos:
open =-30 close =-36 execute =-222 IoErr =-132 mode_old = 1005 alloc_abs =-$cc
ILABEL AssemPro:includes/Amiga.l ;AssemPro only
INIT_AMIGA ;AssemPro only run: bsr init ;initialization bra test ;system test
init: ;system initialization and ;open move.l execbase,a6 ;number of execute-library lea dosname(pc),a1 moveq #0,d0 jsr openlib(a6) ;open DOS-library move.l d0,dosbase beq error
lea consolname(pc),a1 ;console definition move.l #mode_old,d0 bsr openfile ;console open beq error move.l d0,conhandle
rts
test: bsr dir ;do directory bra qu ;quit and exit
dir: move.l dosbase,a6 ;DOS base address in A6 move.l #command,d1 ;pointer to command line clr.l d2 ;no input (CLI window) move.l conhandle,d3 ;output in our window jsr execute(a6) ;execute command rts
error: move.l dosbase,a6 jsr IoErr(a6) move.l d0,d5
move.l #-1,d7 ;flag
qu: move.l conhandle,d1 ;window close move.l dosbase,a6 jsr close(a6)
move.l dosbase,a1 ;DOS.Lib close move.l execbase,a6 jsr closelib(a6)
EXIT_AMIGA ;AssemPro only
openfile: ;open file move.l a1,d1 ;pointer to I/O-Definition- ;text move.l d0,d2 move.l dosbase,a6 jsr open(a6) tst.l d0 rts
dosname: dc.b 'dos.library',0,0 align.w dosbase: dc.l 0 consolname: dc.b 'CON:0/100/640/100/** CLI-Test **',0 align.w conhandle: dc.l 0 command: dc.b "dir",0
end
6.5.6.Read Directory. --------------------- Now,lets look at another method that doesn't need the CLI.In this way,you can read the directory of any disk without having to play Disk Jockey. You need to writ a program that does what CLI's Dir program does. There are several steps. First you must give the system a key to the desired directory.That means you must call DOS'Lock function.It needs two parameters:
In D1 pass a pointer to a text that contains the name of the directory you wish to read.If,for example,you want to read the contents of the RAM disk,the text would be 'RAM:',0. In D2 put the mode that determines whether to read or write.Let us use the "Read"(-2) mode.
You call the Lock function (offset -84) and get either a point to the key or a zero returned to you in the D0 register.If you get a zero,the call did'nt work,the file was'nt found.This function can be used to find if a file is on the disk.You use this function with the name and see if D0 comes back zero.If not,the file exists. Lets assume the file or path exists.You need to save the value that came back in D0.You'll need it for both functions that you'll call. The next function you need is called Examine.You use it to search the disk for an acceptable entry.It returns parameters like name, length and date that correspond to the entry.You need to reserve a memory block for this information and put the beginning of the block in D2 before calling the Examine function.Put the key you got from the Lock function in the D1 register. The memory area that is filled with information is called a FileInfoBlock.Its 260 bytes long and contains information about the file.The name starts in the 9th byte and ends with a null byte so you can easily print it with our "pmsg"routine.The information that Examine gives isn't about a particular file,but about the disk.The name in FileInfoBlock is the disk name. The Examine function sends the status back in the D0 register. Since the Lock function already tested if the file existed,evalua- ting the status really isn't necessary. Now to the function that you can use to read individual files from the directory.The function is called ExNext (Examine Next).This function searches for the next entry that fits the key every time it is called.ExNext gets the same parameters as Examine gets. However,the return parameter in D0 is more important here. The ExNext function is always called in the same way.It always gets the next entry of the directory.If no more entries exist in the directory,ExNext puts a zero in the D0 register. You need to continue performing this operation until there aren't any more entries.You can find this using the IoErr function from the DOS library. This function doesn't need any parameters.It returns the status of the last I/O operation that was performed in the D0 register.After the last ExNext,this value is 232,which means no_more_Entries.
Heres a complete routine for reading the directory of the disk in DFO:and displaying the contents in the window.
; 6.5.5B.ASM ;***** DOS-Sample function 3/87 S.D. *****
openlib =-30-378 closelib =-414 exbase =4
* calls to amiga dos:
open =-30 close =-30-6 read =-30-12 write =-30-18 myinput =-30-24 output =-30-30 currdir =-30-96 lock =-30-54 examine =-30-72 exnext =-30-78 exit =-30-114 IoErr =-30-102 waitforch =-30-174 mode = 0 mode_old = 1005 mode_new = 1006 alloc_abs =-$cc free_mem =-$d2
ILABEL AssemPro:includes/Amiga.l ;AssemPro only
INIT_AMIGA ;AssemPro only
run: bsr init ;initialization bra test ;system-test
init: ;system initialization and ;open move.l exbase,a6 ;pointer to exec.library lea dosname(pc),a1 moveq #0,d0 jsr openlib(a6) ;open dos-library move.l do,dosbase beq error
lea consolname(pc),a1 ;console definition move.l #mode_old,d0 bsr openfile ;console open beq error moveq d0,conhandle
rts
test: move.l #mytext,d0 bsr pmsg ;test-text output
move.l dosbase,a6 move.l #name,d1 move.l #-2,d2 jsr lock(a6) move.l d0,d5 tst.l d0 beq error move.l d0,locksav
move.l dosbase,a6 move.l locksav,d1 move.l #fileinfo,d2 jsr examine(a6) move.l d0,d6 tst.l d0 beq error
loop: move.l dosbase,a6 move.l locksav,d1 move.l #fileinfo,d2 jsr exnext(a6) tst.l d0 beq error
move.l #fileinfo+8,d0 bsr pmsg bsr pcrlf bra loop
error: move.l dosbase,a6 jsr ioerr(a6) move.l d0,d6
move.l #presskey,d0 bsr pmsg bsr getch move.l #-1,d7 ;flag
qu: move.l conhandle,d1 ;window close move.l dosbase,a6 jsr close(a6)
move.l dosbase,a1 ;dos.lib close move.l exbase,a6 jsr closelib(a6) EXIT_AMIGA ;AssemPro only
openfile: ;open file move.l a1,d1 ;pointer to I/O-Definition- ;Text move.l d0,d2 move.l dosbase,a6 jsr open(a6) tst.l d0 rts
pmsg: ;print message (D0) movem.l d0-d7/a0-a6,-(sp) move.l d0,a0 move.l a0,d2 clr.l d3
mess1: tst.b (a0)+ beq mess2 addq.l #1,d3 bra mess1
mess2: move.l conhandle,d1 move.l dosbase,a6 jsr write(a6) movem.l (sp)+,d0-d7/a0-a6 rts
pcrlf: move #10,d0 bsr pchar move #13,d0
pchar: ;character in D0 output movem.l d0-d7/a0-a6,-(sp) ;save all move.l conhandle,d1
pch1: lea chbuff,a1 move.b d0,(a1) move.l a1,d2 move.l #1,d3 move.l dosbase,a6 jsr write(a6) movem.l (sp)+,d0-d7/a0-a6 ;restore all rts scankey: ;test key move.l conhandle,d1 move.l #500,d2 ;wait value move.l dosbase,a6 jsr waitforch(a6) tst.l d0 rts
readin: ;input from keyboard movem.l d0-d7/a0-a6,-(sp) ;registers lea inline+2,a2 ;pointer to input buffer clr.l (a2)
inplop: bsr getchr cmp.b #8,d0 beq backspace cmp.b #127,d0 ;delete? beq backspace bsr pchar ;character output cmp.b #13,d0 beq inputx move.b d0,(a2)+ bra inplop
input: clr.b (a2)+ sub.l #inline,a2 move a2,inline ;length in inline+1 movem.l (sp)+,d0-d7/a0-a6 ;registers rts
backspace: cmp.l #inline,a2 ;at beginning? beq inplop ;yes move.b #8,d0 bsr pchar ;backspace move #32,d0 bsr pchar ;blank move #8,d0 bsr pchar ;backspace clr.b (a2) subq.l #1,a2 bra inplop
getchr: ;get 1 character from keyboard move.l #1,d3 ;1 character move.l conhandle,d1 lea inbuff,a1 ;buffer-address move.l a1,d2 move.l dosbase,a6 jsr read(a6) clr.l d0 move.b inbuff,d0 rts
mytext: dc.b 'Directory of Diskette: DFO:',10,13,10,13,0,0 dosname: dc.b 'dos.library',0,0 presskey: dc.b 'Press thr Return key!!',0 align.w dosbase: dc.l 0 consolname: dc.b 'CON:0/100/640/100/** Directory-Test **',0 name: dc.b 'DFO:',0 align.w locksav: dc.l 0 fileinfo: ds.l 20 conhandle: dc.l 0 inbuff: DS.B 8 inline: DS.B 180 chbuff DS.B 82
end
The FileInfoBlock contains the following entries:
Offset Name Meaning ---------------------------------------------------------------- 0 DiskKey.L Disk Number 4 DieEntryType.L Entry Type (+=Directory,-=File) 8 FileName 108 bytes with the filename 116 Protection.L File Protected? 120 EntryType.L Entry type 124 Size.L Length of file in bytes 128 NumBlocks.L Number of blocks 132 Days.L Creation day 136 Minute.L Creation time 140 Tick.L Creation time 144 Comment 116 bytes with comments
If you want to have the program output the file length as well,you can read the length with "move.l fileinfo+124,d0"and then use a conversion routine to produce a decimal number.You can output this result with the name.
6.5.7.Direct Access To Disk. ---------------------------- There isn't a simple function in the library for accessing single disk sectors.Here,you must work with a device just like you did with speech output.This time you'll be working with the trackdisk. device. You want to work with this device to directly program the disk drives.Once you've built up the necessary program machinery,you can experiment with various commands for disk access.Remember that an error can cause the disk to be modified and thus unusable.Make sure you're using a non-essential disk.Don't use one which contains your only copy of something. The initialization here is similar to that for speech output.Here is the initialization routine for your program:
;** Direct disk access via trackdisk.device ** (6.5.7)
openlib =-408 closelib =-414 execbase = 4 open =-30 close =-36 opendevice =-444 closedev =-450 sendIo =-462 read =-30-12 write =-30-18 waitforch =-30-174 mode_old = 1005
run: bsr init ;initialization bra test ;system-test
init: ;initialize and open system move.l execbase,a6 ;pointer to exec.library lea dosname,a1 moveq #0,d0 jsr openlib(a6) ;open dos.library move.l d0,dosbase beq error lea diskio,a1 ;pointer to disk I/O area move.l #diskrep,14(a1) ;pointer to port clr.l d0 ;drive 0 (built in) clr.l d1 ;no flags lea trddevice,a0 ;pointer to device name jsr opendevice(a6) ;open trackdisk.device tst.l d0 ;error? bne error ;yes! move.l #consolname(pc),d1 ;console definition move.l #mode_old,d2 ;old mode move.l dosbase,a6 ;dos base address jsr open(a6) ;open window tst.l d0 ;error? beq error ;yes! move.l d0,conhandle ;else save handle rts ;done
test: ;place for test routine
And now for the functions that take care of the various messages at the end of the program.
error: move.l #-1,d7 ;flag for error (for SEKA)
qu: move.l execbase,a6 ;exec base address lea diskio,a1 ;pointer to disk I/O move.l 32(a1),d7 ;IO_ACTUAL in D7 (for testing) move #9,28(a1) ;command motor on/off move.l #0,36(a1) ;0=off,1=on,so turn motor jsr sendio(a6) ;off move.l conhandle,d1 ;close window move.l dosbase,a6 jsr close(a6) move.l dosbase,d1 ;close dos.lib move.l execbase,a6 jsr closelib(a6) lea diskio,a1 jsr closedev(a6) ;close trackdisk.device rts
Lets not forget the routine that waits for the user to press <Return>,so that you can watch the effects of the test function in peace:
getchr: ;get a character from keyboard move.l #1,d3 ;1 character move.l conhandle,d1 ;window handle move.l #inbuff,d2 ;buffer address move.l dosbase,a6 ;dos base address jsr read(a6) ;read character rts ;thats it
The last thing you need is the section of code that declares the text and data fields that your program needs: dosname: dc.b 'dos.library',0 align consolname: dc.b 'RAW:0/100/640/50/** Wait window',0 align trddevice: dc.b 'trackdisk.device',0 align dosbase: dc.l 0 ;dos base address conhandle: dc.l 0 ;window handle inbuff: blk.b 80,0 ;keyboard buffer diskio: blk.l 20,0 ;I/O structure diskrep: blk.l 8,0 ;I/O port diskbuff: blk.b 512*2,0 ;place for 2 sectors
There,now you've done with the set up work.Lets look at how you can give commands to the disk drives.The first and easiest command is the one for turning the drive motor on and off.You've already seen this command in the program.This is command number nine.This number goes in the command word of the I/O structure (bytes 28 and 29 of the structure). You need to pass a parameter that lets the computor know whether to turn the motor off or on.This information goes in the I/O long word that starts at byte 36:its zero for off,and one for on. You already chose the motor that should be turned on or off when you opened the device.You put the number of the chosen disk drive in D0-in your case you put a zero there because you are using the DFO:disk drive. Heres an overview of the commands you can use to access information on the disk:
No Name Function ----------------------------------------------------------------- 2 READ Read one or more sectors 3 WRITE Write sectors 4 UPDATE Update the track buffer 5 CLEAR Erase track buffer 9 MOTOR Turn motor on/off 10 SEEK Search for a track 11 FORMAT Format tracks 12 REMOVE Initialize routine that is called when you remove the disk 13 CHANGENUM Find out number of disk changes 14 CHANGESTATE Test if disk is in drive 15 PROTSTATUS Test if disk is write protected
You've already learned about command number nine.Lets look at the three commands you can use to make tests.These are the last three commands.They put a return value in the long word that begins in the 32nd byte in the I/O structure.This value was written in D7 in the program above for testing purposes.You can read its contents directly if you ran the program with AssemPro. Here is a simple routine that you can use to run one of these commands with:
test: ; (6.5.7B) lea diskio,a1 ;pointer to I/O structure move #13,28(a1) ;pass command (for example 13) move.l execbase,a6 ;execbase address in A6 jsr sendio(a6) ;call function
If CHANGENUM (command 13) is executed,in D7 you'll get the number of times a disk was taken out and put in the drive.If you call the program,you'll get a value back.If you take the disk out and put it back in,the number is two higher the next time you call the program. The CHANGESTATE command (command 14) tells whether a disk is in the drive or not.If one is,a zero comes back.Otherwise,a $FF is returned. You get the same values back from the PROTSTATUS function (command 15).Here a zero means that the disk isn't write protected,while $FF means that it is. Now lets look at the READ and WRITE functions.These operations need a few more parameters than the status functions.You need to pass the following parameters:
The address of the I/O buffer in the data pointer,the number of bytes to be transfered in I/O length,and the data address on the disk in I/O offset.
The number of data bytes must be a multiple of 512,since every sector is 512 bytes,and only whole sectors can be read.
The data address is the number of the first byte in the sector.If you want to use the first sector,the offset is zero.For the second sector,its 512,etc...The formula is:
offset = (sector_number -1) *512
Here is a routine that loads the first two sectors of the disk into the buffer:
test: (6.5.7C) lea diskio,a1 move #2,28(a1) ;command:READ move.l #diskbuff,40(a1) ;buffer move.l #2*512,36(a1) ;length:2 sectors move.l #0*512,44(a1) ;offset:0 sectors move.l execbase,a6 ;exec base address jsr sendio(a6) ;start function
Start the program from the debugger and then look at the buffers contents after the program ends.You can find out the format of the disk here.If you want to read a sector thats being used,change the 0 in the offset definition to 700 and start again.Its highly probable that theres some data there. To modify and write back the data that you've read from the disk, you need command number three,the WRITE command.The parameters are the same. If you've executed the WRITE commandyou're probably wondering why the disk light did'nt go on.Thats because the Amiga writes a track that as been read into a buffer on its own.It WRITE's data there as well.It won't write the data to disk until another track is accessed. You can have the data updated directly as well using command four, the UPDATE command. Command 11,the FORMAT command,is also quite interesting.This command needs a data field that is 11*512=5632 bytes long-the length of a track.The offset must be a multiple of this number so that you start at the beginning of a track. The length must be a multiple of 5632 as a result.If several tracks are formatted,each track is filled with the same data. You can use this function to easy write a disk copy program.You READ the source disk and then FORMAT the corresponding track on the destination disk.Thats how the DiskCopy program works-it reformats the destination disk. Command ten,the SEEK command,just needs the offset.It moves the Read/Write head of the drive to the position specified without making a disk access or testing if its at the right position. Command 12,the REMOVE command,is used to install an interrupt routine that is called when the disk is removed from the disk drive.The address of the interrupt structure is passed in the data pointer of the I/O structure.If theres a zero here,the interrupt routine is turned off.
Heres a complete example program in AssemPro format:
;***** Track disk-Basic function 10/86 S.D. *****
ILABEL ASSEMPRO:includes/Amiga.l :AssemPro only
openlib =-30-378 closelib =-414 ;execbase = 4 ;defined in INIT_AMIGA
* calls to amiga dos:
open =-30 close =-30-6 opendevice =-444 closedev =-450 sendIo =-462 read =-30-12 write =-30-18 waitforch =-30-174 mode_old = 1005
INIT_AMIGA ;AssemPro only
run: bsr init ;initialization bra test ;system test
init: ;system initialization and ;open move.l execbase,a6 ;pointer to exec-library lea dosname,a1 moveq #0,d0 jsr openlib(a6) ;open dos-library move.l d0,dosbase beq error
lea diskio,a1 move.l #diskrep,14(a1) clr.l d0 clr.l d1 lea trddevice,a0 jsr opendevice(a6) ;open trackdisk.device tst.l d0 bne error
bp: lea consolname(pc),a1 ;console-definition move.l #mode_old,d0 bsr openfile ;console open beq error move.l d0,conhandle
rts
test: bsr accdisk
bsr getchr ;wait for character bra qu
error: move.l #-1,d7 ;flag
qu: move.l execbase,a6 lea diskio,a1 move #9,28(a1) move.l #0,36(a1) jsr sendio(a6)
move.l conhandle,d1 ;window close move.l dosbase,a6 jsr close(a6)
move.l dosbase,a1 ;dos.lib close move.l execbase,a6 jsr closelib(a6)
lea diskio,a1 move.l 32(a1),d7 jsr closedev(a6)
EXIT_AMIGA ;AssemPro only
openfile: ;open file move.l a1,d1 ;pointer to the I/O-definition ;text move.l d0,d2 move.l dosbase,a6 jsr open(a6) tst.l d0 rts
scankey: ;test for key move.l conhandle,d1 move.l #500,d2 ;wait value move.l dosbase,a6 jsr waitforch(a6) tst.l d0 rts
getchr: ;get one character from ;keyboard move.l #1,d3 ;1 character move.l conhandle,d1 lea inbuff,a1 ;buffer-address move.l a1,d2 move.l dosbase,a6 jsr read(a6) clr.l d0 move.b inbuff,d0 rts
accdisk: lea diskio,a1 move #2,28(a1) ;command:READ move.l #diskbuff,40(a1) ;buffer move.l #2*512,36(a1) ;length:2 sectors move.l #20*512,44(a1) ;offset: n sectors move.l execbase,a6 jsr sendio(a6) rts
dosname: dc.b 'dos.library',0,0 align.w dosbase: dc.l 0 consolname: dc.b 'RAW:0/100/640/100/** Test-Window S.D.V0.1',0 trddevice: dc.b 'trackdisk.device',0 align.w conhandle dc.l 0 inbuff: ds.b 8 diskio: ds.l 20,0 diskrep: ds.l 8,0 diskbuff: ds.b 512*2,0 end