(c)  Copyright 1989 Commodore-Amiga, Inc.   All rights reserved.
The information contained herein is subject to change without notice, and 
is provided "as is" without warranty of any kind, either expressed or implied.  
The entire risk as to the use of this information is assumed by the user.



                  Assembler Device IO - The Easy Way

                       Carolyn Scheppner
                        

   Amiga.lib contains a number of compiled C Exec support functions which
are used by C programmers to simplify the creation of tasks and ports
and the setup of IORequest structures.  In addition, amiga.lib contains
a number of C support functions such as printf() and getchar().  These
functions are all documented in the Addison-Wesley Exec manual in 
Appendix B, along with the C source of the Exec support functions.

   Although these functions are only provided with a C interface, it is
relatively easy to call them from assembler.  The Exec support functions
can greatly simplify assembler device IO and task creation code, and the
C support functions such as printf() are handy for simple formatted output
and debugging.  Note that to use the amiga.lib stdio functions, you must
link with Astartup.obj or set up your own Amiga stdio file handles.

   The general method for accessing the C interface amiga.lib functions
from assembler is as follows:

     1. XREF the function name prepended with an underscore ("_")

     2. Look at the C calling conventions for the function.
        You will have to push the required arguments onto the stack
        as longs, in right to left order as they appear in the C call.
        If an argument is a literal string or string pointer, you
        must push the address of a string that you have terminated
        with a null. (See fstr DC.B in the DevIO.asm example.)

     3. Assume that the contents of d0-d1/a0-a1 will be lost, so save
        them if necessary.  Push your arguments, as longs, onto the stack.
        JSR _functionname, then add 4 to the stack pointer for each long
        that you pushed on the stack.  The function result is returned
        in D0.


   The DevIO.asm example source in this issue is commented so that you can 
compare the C calling conventions with the assembler equivalents.


 
        
---------------------------  Example  -------------------------------

*  DevIO.asm
*
*  Opening and talking to a device in mostly asm  - Carolyn Scheppner
*
*  Copyright 1988 Commodore-Amiga, Inc.  All Rights Reserved
*
*  Opens gameport.device unit 0 and asks gameport what controller type
*     gameport 0 is set to  (1=mouse)
*
*  Demonstrates assembler use of the compiled C exec support
*  routines (CreatePort, etc.) in amiga.lib, and also the use of
*  amiga.lib csupport functions such as _printf for simple formatted
*  output and debugging.
*
*  Outputs   address of port
*            address of iob
*            return of Opendevice (0=success)
*            controller type (1=mouse)
*
*  LINK INSTRUCTIONS: Alink with Astartup.obj ... LIBRARY amiga.lib
*   (Astartup sets up the stdout needed for amiga.lib _printf, etc.)
*

   INCLUDE "exec/types.i"
   INCLUDE "exec/io.i"
   INCLUDE "libraries/dos.i"
   INCLUDE "devices/gameport.i"

** Imported Labels **

*------ from Astartup.obj
   XREF     _AbsExecBase
   XREF     _SysBase

*------ System function Library Vector Offsets 
   XREF     _LVOOpenDevice
   XREF     _LVOCloseDevice
   XREF     _LVOSendIO
   XREF     _LVOWait
   XREF     _LVOGetMsg

*------ C interface Exec Support functions in amiga.lib 
   XREF     _CreatePort
   XREF     _DeletePort
   XREF     _CreateStdIO
   XREF     _DeleteStdIO

*------ C interface C Support functions in amiga.lib 
   XREF     _getchar
   XREF     _printf

** Exported Labels **

*------ Where Astartup.obj JSR's to our code
   XDEF     _main

            CODE

** Code **

_main:
            movem.l   d2-d7/a2-a6,-(sp)  ;Save the registers

*----- Exec Support function:  msgPort = CreatePort(name,pri)
*      Used to create the replyPort for our messages

            move.l    #0,-(sp)       ;push priority 0 on stack as long
            pea       portname       ;push addr of my null-termed portname
            jsr       _CreatePort    ;call CreatePort
            addq.l    #8,sp          ;add 4 to stack for each long pushed
            jsr       mydebug0       ;my print d0 debugging rtn
            move.l    d0,myport      ;save result
            beq       failure        ;if zero, CreatePort failed

*----- Exec Support function:  ioReq = CreateStdIO(ioReplyPort)
*      Note - many other devices use special extended IO requests
*             which are defined in the include file for the device.
*             If a larger request is required, you must use
*             _CreateExtIO(ioReplyPort,size) and _DeleteExtIO(ioReq) 

            move.l    d0,-(sp)       ;push d0 (still holds port from above)
            jsr       _CreateStdIO   ;call CreateStdIO
            addq.l    #4,sp          ;add 4 to stack for the long pushed
            jsr       mydebug0       ;my print d0 debugging rtn
            move.l    d0,myiob       ;save result
            beq       failure        ;if zero, CreateStdIO failed

*----- The normal assembler _LVO method is used to call system routines

*----- Open the device

            move.l    d0,a1          ;iorequest returned above
            lea.l     devname,a0     ;null terminated device name
            moveq     #0,d0          ;unit 0
            moveq     #0,d1          ;flags
            move.l    _SysBase,a6           ;prepare to call Exec function
            jsr       _LVOOpenDevice(a6)    ;call OpenDevice
            jsr       mydebug0              ;my print d0 debugging rtn
            tst.l     d0             ;check for 0 (success) of OpenDevice
            bne       failure        ;branch if OpenDevice failed
            move      #1,gotdev      ;else set flag that it was successful

*----- Build waitmask of 1 << myport->mp_SigBit

            moveq     #0,d0             ;clear d0
            movea.l   myport,a0         ;myport to a1
            move.b    MP_SIGBIT(a0),d0  ;get sigbit number from myport
            moveq     #1,d1             ;put 1 in d1
            lsl.l     d0,d1             ; and shift left  (1 << sigbit)
            move.l    d1,waitmask       ; to create waitmask for our Wait

*----- Set up our command as specified in gameport device autodocs

            movea.l   myiob,a1          ;myiob to a1 to set up command
            move.w    #GPD_ASKCTYPE,IO_COMMAND(a1)  ;our command
            move.l    #1,IO_LENGTH(a1)              ;as per autodocs
            move.l    #result,IO_DATA(a1)   ;address of byte for result

*----- Send the command, Wait for the reply, the GetMsg the reply
*      Note that 

            move.l    _SysBase,a6       ;set up for an Exec call
            jsr       _LVOSendIO(a6)    ;call SendIO, myiob already in a1

            move.l    waitmask,d0       ;CreatePort'd port is a signal port
            jsr       _LVOWait(a6)      ;So I wait for the signal

            movea.l   myport,a0         ;then get the message
            jsr       _LVOGetMsg(a6)
             
            moveq     #0,d0             ;clear d0 since result is a byte
            move.b    result,d0         ;move result to d0
            jsr       mydebug0          ;my print d0 debugging rtn

*----- If you uncomment this, program will wait till you hit <RETURN>
*      A loop of _getchar calls can be used to simulate a gets().
*      Just store each d0 returned in an array until you get a 10 ('\n').
*      Then null terminate the string.
*          jsr       _getchar    ;waits for <RET> in case you want to wack

*----- Code was successfully executed
            move.l    #RETURN_OK,retcode    ;set up success return code
            bra.s     cleanup               ;and skip to cleanup code

*----- Failures in earlier code will branch here
failure:
            move.l    #RETURN_FAIL,retcode  ;set up success return code

cleanup:
            tst.l     gotdev            ;if OpenDevice was unsuccessful
            beq       nodev             ;  skip CloseDevice
            move.l    _SysBase,a6       ;else set up for Exec call
            move.l    myiob,a1               ;ioReq to a1
            jsr       _LVOCloseDevice(a6)    ;call CloseDevice
nodev
            move.l    myiob,d0          ;if CreateStdIO was unsuccessful
            beq       noiob             ;  skip DeleteStdIO

*----- Exec Support function:  DeleteStdIO(ioReq)
            move.l    d0,-(sp)          ;else push d0 (now our ioReq)
            jsr       _DeleteStdIO      ;call DeleteStdIO
            addq.l    #4,sp             ;add 4 to stack for pushed long
noiob
            move.l    myport,d0         ;if CreatePort was unsuccessful
            beq       noport            ;  skip DeletePort

*----- Exec Support function:  DeletePort(port)
            move.l    d0,-(sp)          ;else push d0 (now our msgPort)
            jsr       _DeletePort       ;call DeletePort
            addq.l    #4,sp             ;add 4 to stack for pushed long
noport

            movem.l   (sp)+,d2-d7/a2-a6        ;Restore registers
            move.l    retcode,d0               ;Put our return code in d0
            rts                                ;rts


*----- mydebug0 - uses amiga.lib _printf to print the contents of d0
*                 I preserve d0-d1/a0-a1/a6
mydebug0:     
            movem.l   d0-d1/a0-a1/a6,-(sp)     ;save these registers

*----- C Support function printf(): here  printf("$%lx\n",contents_of_d0)
*      Note that the fstr DC.B below specifies '\n' and null as 10,0

            move.l    d0,-(sp)                 ;push d0 on the stack
            pea       fstr                     ;push addr of format string
            jsr       _printf                  ;call printf
            addq.l    #8,sp                    ;add 4 to stack for each long
            movem.l   (sp)+,d0-d1/a0-a1/a6     ;restore the saved registers
            rts                                ;rts


            DATA

            CNOP  0,4
myiob       DC.L  0
myport      DC.L  0
gotdev      DC.L  0
waitmask    DC.L  0
retcode     DC.L  0
result      DC.B  0
portname    DC.B  'cas_devport',0
devname     DC.B  'gameport.device',0
fstr        DC.B  '$%lx',10,0
            END



