[comp.sys.amiga.tech] 680x0 assembly questions

lphillips@lpami.wimsey.bc.ca (Larry Phillips) (04/06/90)

In <1990Apr6.202242.13920@mintaka.lcs.mit.edu>, rlcarr@athena.mit.edu (Rich Carreiro) writes:
 >Question for all you net.kind.knowledgable.souls our there:
 >
 >Do you know of any books that:
 >a) deal with the 680x0

680x0 Programming by Example - Stan Kelly-Bootle - Sams
Compute!'s Amiga machine Language Programming Guide

 >b) *TEACH* you assembly along the way [i.e. I've never dealt direct with it
 >                                       *HORRORS* :-]

For generic 680x0 programming, the 'by Example' book mentioned above is a good
one to learn from. The Compute! book, despite the title, is really about
assembler, and will show you the techniques required to write assembler on the
AMiga, specifically.

 >Also, what is needed to run code under CPR?  If I have some simple
 >program which just decrements some 680x0 data register, to I still have to
 >blink it to c.o?

I don't know much about writing assembler with the Lattice assembler, and all
the ins and outs of what you need to BLink with. CPR will handle assembler
programs generated with A68K, HASM, and probably C.A.P.E.

 >I guess what I am wondering is if I just want to do generic 680x0 programming
 >[i.e. no Amiga system calls] does such a program need startup code, or can
 >I just assemble it and run it through blink (without c.o or any of that) and
 >then run it under the debugger.

Startups are strictly a convenience, and if you don't require the services they
provide, you do not need to link with them in place. For example, the following
code, in a file called test.a ...

start	moveq.l	#10,d0
			rts
			end

will assemble with A68K, with the command line:

a68K test.a

and the final executable can be generated using BLink ...

blink test.o

The program doesn't do much, of course, but shows that the startup code is not
needed. Even if you want to do some output, parse command lines, and so on, you
can still get by without startup code if you are willing to write your own
routines to do what you need. Here is a little thing I put together for a
seminar for beginning assembler programmers. It shows you how to get the
'handle' for stdout and how to call an Amiga library routine. It also shows how
to assemble a program without having to mess with includes or linking with link
libs. I would warn you though, that you must be careful of doing this sort of
thing, since new includes might change something you took for granted.

Save it as 'hello.asm'. To assemble and run, use the following command lines:

a68K hello.asm
blink hello.o

Here's the code:

*--------------------------
* Our first program - Hello
*--------------------------

*---------------------------------------------------------------
* We start with comments, as above, and in this line.
* A comment is preceded by an '*' character, a ';' character,
* or by virtue of it being in the comment field. I like to use
* the '*' at the beginning of the line, and the ';' for comments
* not starting in the first column. Some assemblers are picky
* about when the '*' and ';' are used.
*---------------------------------------------------------------

*---------------------------------------------------------
* These equates are here so that we don't have to include
* the standard Amiga include files, or link with amiga.lib
* an equate (EQU) simply sets the name in the label to a
* value.
*----------------------------------------------------------

_LVOOpenLibrary   EQU     $fffffdd8
_LVOCloseLibrary  EQU     $fffffe62
_LVOOutput        EQU     $ffffffc4
_LVOWrite         EQU     $ffffffd0

*---------------------------------------------------------------------
* This is the main part of the program, the code itself.
* We have to set a few things up before actually sending our greeting.
* This is where we do it.
*---------------------------------------------------------------------

start   move.l  4,a6                ; move the exec lib pointer to a6
        move.l  #dosname,a1         ; get pointer to lib name
        moveq   #0,d0               ; allow any revision level of dos lib
        jsr     _LVOOpenLibrary(a6) ; Open the library
        tst.l   d0                  ; see if library opened
        beq     exit                ; exit if lib did not open

        move.l  d0,dos              ; save a copy of dos pointer
        move.l  d0,a6               ; put library base pointer into a6
        jsr     _LVOOutput(a6)      ; get stdout handle
        tst.l   d0                  ; test to see if handle returned
        beq     cleanup             ; cleanup/exit if not

        move.l  d0,stdout           ; save pointer to stdout

*-----------------------------------------------------
* Here we actually send the text to the screen.
* This is the part of the program that 'does the work'
*-----------------------------------------------------

        move.l  stdout,d1       ; send it to stdout
        move.l  #msg,d2         ; address of message to d2
        move.l  #msglen,d3      ; tell the Write routine how long
                                ; the string is.
        jsr     _LVOWrite(a6)   ; do the Write

*---------------------------------------------------
* All finished. Remember Mom telling you to clean up
* after yourself? Well, thats what we do now.
*---------------------------------------------------

cleanup move.l  4,a6                 ; get the exec lib pointer again
        move.l  dos,a1               ; and the dos lib pointer
        jsr     _LVOCloseLibrary(a6) ; and close the dos lib

exit    rts                          ; we're done.

*----------------------------------------------------------
* This is the start of a SECTION.
* It will contain only data or uninitialized storage
* areas that we need for our program.
*----------------------------------------------------------

        SECTION DATA

dosname dc.b    'dos.library',0     ; dos lib name for open
msg     dc.b    'Hello, world!',10  ; message to print
msglen  EQU     *-msg               ; defines length of message

        CNOP    0,4                 ; this makes sure the next declaration
                                    ; starts on a longword boundary.

stdout  dc.l    1                   ; storage for stdout pointer
dos     dc.l    1                   ; and dos pointer
        END

*-----------------------------------


 >And let's say I want to do a bubble sort or something.  WOuld I have to call
 >AllocMem(), or do the dc.* assembler directives take care of reserving data
 >space?

You can AllocMem, or use the DC or DS directives, as you see fit. It really
depends on what you want to do.. how you are going to get the data in there,
and so on. Using the system calls is not at all difficult, but it will take you
time to learn them. It's definitely worth learning for anything past the
fundamental assembler stage.

 >And in programs that do call Amiga specific stuff, is the c.o startup needed?
 >When IS c.o needed and when is it not?

 >  What about running generic assembly from the CLI?

Any startup/initialization stuff is only needed when you need what it provides.
A program will start at the first instruction within the program, and can be
run directly from the CLI by just typing its name.

Part of the startup code in C programs takes care of getting to the 'intended'
first instruction, which is always the first instruction in the _main()
function.  In an assembler program that you write without a startup module, you
control what happens, completely.

 >The last time I diddled with assembly was on my 6809 based CoCo, and since it
 >was not multitasking (I didn't buy OS-9) there was't startup.  You just
 >used a BASIC command that put the program counter at the beginning of you
 >code and then you hope it reached the final RTS :-)

For the most part, you can write a program as if it were the only program in
the machine. You should be aware of what is considered 'proper' behaviour of a
program, and should always ask for resources in the system approved manner.
ie. you ask for memory via AllocMem, and then you _CHECK_ to see that the OS
granted your request, and finally, you make sure that you only use that memory.
If you 'run off the end', you will be stomping on memory you don't own.

 >A question for those with the 1.3 RKM's.
 >In the Libs and Devs, in the section on Workbench, there is the assembly 
 >source to a Amiga-only startup - i.e. it only inits the AmigaDOS I/O
 >(i.e. the stdio built into the Amiga ROMs *NOT* the Lattice stdio in lc.lib).
 >Now - if I want such a startup, do I have to type that in, or is there an
 >equivalent in Lattice?  Some #define that can be made?  Or a recompilation
 >of c.a or main.c?

If you want to do any writing to stdout, you must:

	get the address of exec.library
	open the dos library
	find stdout

This is accomplished by either writing your own (as in the example source
above), or by including a prewritten startup module with BLink.

 >And I apologize if these are baby questions, but not everyone's a guru.

They aren't 'baby' questions at all. Everyone who has done any programming in C
or assembler on the Amiga has been faced with these same questions, and your
background prior to writing for the Amiga will determine how easily and quickly
you can pick up the concepts.

 >And finally, should I just use Lattice's assembler and CPR, or should I
 >get A68K or whatever the PD/SHareware assembler is?

I highly recommend A68K, for a low cost solution to assembly programming. For a
faster assembler, you might consider HiSoft's DevPak, C.A.P.E., or ArgAsm. The
only one I would not recommend is the Abacus Assempro. You cannot link the
programs you build with Assempro, and their includes are completely
non-standard.

On the subject of includes, you can use Lattice's '.i' files, or you can get
the 'Native Developer's Upgrade' directly from CBM for about $25. It contains
the latest includes, autodocs, and a few other goodies.

 >Thanks!!

Hope this helps some. Breaking into assembler can be a character building
experience. :-)

-larry

--
Entomology bugs me.
+-----------------------------------------------------------------------+ 
|   //   Larry Phillips                                                 |
| \X/    lphillips@lpami.wimsey.bc.ca -or- uunet!van-bc!lpami!lphillips |
|        COMPUSERVE: 76703,4322  -or-  76703.4322@compuserve.com        |
+-----------------------------------------------------------------------+

markv@kuhub.cc.ukans.edu (04/08/90)

> Do you know of any books that:
> a) deal with the 680x0
> b) *TEACH* you assembly along the way [i.e. I've never dealt direct with it
>                                        *HORRORS* :-]

Not personally.  I learned 680X0 assembler in an upper class systems
programming language.
 
> Also, what is needed to run code under CPR?  If I have some simple
> program which just decrements some 680x0 data register, to I still have to
> blink it to c.o?

No, you don't need c.o.  c.o is mainly for C programs.  It opens the
standard file handles as streams (Amiga DOS already has them open as
Amiga DOS files for CLI programs) and handles the WB startup message,
and builds the argv array.

In assembler, AmigaDOS gives you most of what you need.  The pointers
to DOSBase and the command line are in registers (dont remember which
ones).  You can get amigados stdin and stdout with calls to Input()
and Output().

> I guess what I am wondering is if I just want to do generic 680x0 programming
> [i.e. no Amiga system calls] does such a program need startup code, or can
> I just assemble it and run it through blink (without c.o or any of that) and
> then run it under the debugger.  What about running generic assembly from the
> CLI?

As just said, no.  You can just jump right into your code.  If not
using the startup you need to open libraries yourself, but it works
fine.  To exit the code call DOS Exit() or do a rts at the end of your
code.
 
> And let's say I want to do a bubble sort or something.  WOuld I have to call
> AllocMem(), or do the dc.* assembler directives take care of reserving data
> space?

The ds.* and dc.* work just fine, just remember that this is static
data.  If you want your code reentrant, allocate your storage on the
stack or with AllocMem.
 
> And in programs that do call Amiga specific stuff, is the c.o startup needed?
> When IS c.o needed and when is it not?

No, you still dont need c.o.  c.o really is mainly for c programs
only.  You can open the libraries yourselves and make system calls
yourself and do anything you want.  
 
> The last time I diddled with assembly was on my 6809 based CoCo, and since it
> was not multitasking (I didn't buy OS-9) there was't startup.  You just
> used a BASIC command that put the program counter at the beginning of you
> code and then you hope it reached the final RTS :-)

That works on the Amiga.  Exit() allows you do do an safe exit
regardless of how many subroutine jumps your nested, you don't need to
back out if you call Exit().  Just remember, free anything YOU
allocate and dont free anything you dont.  For instance, Input() gives
you the stdin file handle, but you didn't open them yourself, so you
dont close it.
 
> A question for those with the 1.3 RKM's.
> In the Libs and Devs, in the section on Workbench, there is the assembly 
> source to a Amiga-only startup - i.e. it only inits the AmigaDOS I/O
> (i.e. the stdio built into the Amiga ROMs *NOT* the Lattice stdio in lc.lib).
> Now - if I want such a startup, do I have to type that in, or is there an
> equivalent in Lattice?  Some #define that can be made?  Or a recompilation
> of c.a or main.c?

Lattice used to have a startup what used AmigaDOS file handles (called
a.o).  Such a thing doesn't exist, but can be done.  Type it in, and
use the Amiga I/O calls in amiga.lib (Printf, Sprintf, etc).  Or, you
can #define calls like this that use Amiga DOS handles by using Exec's
RawDoFmt or sprintf, and then pass the string to Write (Amiga DOS) or
_drwrite() (lattice).
 
> Also, is there anyway to make a C program *WITHOUT* needijng any sort of
> startup?  I remember something like that in one of the examples that comes
> with Lattice.

It works, you just need to realize that at entry NOTHING is done for
you and at exit nothing will be done.  (Most importantly any memory
allocated by lc.lib (malloc and calloc and realloc) will NOT be freed.
Same goes for Stream file handles.  You need to use AllocMem and the
DOS calls for I/O.

> Any help on any of this stuff will be GREATLY appreciated.
> 
> And I apologize if these are baby questions, but not everyone's a guru.
> 
> And finally, should I just use Lattice's assembler and CPR, or should I
> get A68K or whatever the PD/SHareware assembler is?

If you wan't to use CPR (and it DOES help) you need to use Lattice's
assembler so the symbolic debugging inforamtion gets put in the file.
 
> Thanks!!
 
Try looking at the example code out there, it is the best teacher
(especially on things like the semantics of assembler system calls).

If you want I can give you more specific details.
 
> --
> Hollywood's Animato Lives!                          ==>  Mike Jittlov  <==
> ARPA: rlcarr@space.mit.edu                                   is
> UUCP: ...!mit-eddie!space.mit.edu!rlcarr             ** The Wizard of **
> BITNET: rlcarr@space.mit.edu                        *** Speed and Time ***
-- 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Mark Gooderum			Only...		\    Good Cheer !!!
Academic Computing Services	       ///	  \___________________________
University of Kansas		     ///  /|         __    _
Bix:	  markgood	      \\\  ///  /__| |\/| | | _   /_\  makes it
Bitnet:   MARKV@UKANVAX		\/\/  /    | |  | | |__| /   \ possible...
Internet: markv@kuhub.cc.ukans.edu
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~