Current version

v1.10.4 (stable)

Navigation

Main page
Archived news
Downloads
Documentation
   Capture
   Compiling
   Processing
   Crashes
Features
Filters
Plugin SDK
Knowledge base
Donate
Contact info
Forum
 
Other projects
   Altirra

Search

Archives

01 Dec - 31 Dec 2013
01 Oct - 31 Oct 2013
01 Aug - 31 Aug 2013
01 May - 31 May 2013
01 Mar - 31 Mar 2013
01 Feb - 29 Feb 2013
01 Dec - 31 Dec 2012
01 Nov - 30 Nov 2012
01 Oct - 31 Oct 2012
01 Sep - 30 Sep 2012
01 Aug - 31 Aug 2012
01 June - 30 June 2012
01 May - 31 May 2012
01 Apr - 30 Apr 2012
01 Dec - 31 Dec 2011
01 Nov - 30 Nov 2011
01 Oct - 31 Oct 2011
01 Sep - 30 Sep 2011
01 Aug - 31 Aug 2011
01 Jul - 31 Jul 2011
01 June - 30 June 2011
01 May - 31 May 2011
01 Apr - 30 Apr 2011
01 Mar - 31 Mar 2011
01 Feb - 29 Feb 2011
01 Jan - 31 Jan 2011
01 Dec - 31 Dec 2010
01 Nov - 30 Nov 2010
01 Oct - 31 Oct 2010
01 Sep - 30 Sep 2010
01 Aug - 31 Aug 2010
01 Jul - 31 Jul 2010
01 June - 30 June 2010
01 May - 31 May 2010
01 Apr - 30 Apr 2010
01 Mar - 31 Mar 2010
01 Feb - 29 Feb 2010
01 Jan - 31 Jan 2010
01 Dec - 31 Dec 2009
01 Nov - 30 Nov 2009
01 Oct - 31 Oct 2009
01 Sep - 30 Sep 2009
01 Aug - 31 Aug 2009
01 Jul - 31 Jul 2009
01 June - 30 June 2009
01 May - 31 May 2009
01 Apr - 30 Apr 2009
01 Mar - 31 Mar 2009
01 Feb - 29 Feb 2009
01 Jan - 31 Jan 2009
01 Dec - 31 Dec 2008
01 Nov - 30 Nov 2008
01 Oct - 31 Oct 2008
01 Sep - 30 Sep 2008
01 Aug - 31 Aug 2008
01 Jul - 31 Jul 2008
01 June - 30 June 2008
01 May - 31 May 2008
01 Apr - 30 Apr 2008
01 Mar - 31 Mar 2008
01 Feb - 29 Feb 2008
01 Jan - 31 Jan 2008
01 Dec - 31 Dec 2007
01 Nov - 30 Nov 2007
01 Oct - 31 Oct 2007
01 Sep - 30 Sep 2007
01 Aug - 31 Aug 2007
01 Jul - 31 Jul 2007
01 June - 30 June 2007
01 May - 31 May 2007
01 Apr - 30 Apr 2007
01 Mar - 31 Mar 2007
01 Feb - 29 Feb 2007
01 Jan - 31 Jan 2007
01 Dec - 31 Dec 2006
01 Nov - 30 Nov 2006
01 Oct - 31 Oct 2006
01 Sep - 30 Sep 2006
01 Aug - 31 Aug 2006
01 Jul - 31 Jul 2006
01 June - 30 June 2006
01 May - 31 May 2006
01 Apr - 30 Apr 2006
01 Mar - 31 Mar 2006
01 Feb - 29 Feb 2006
01 Jan - 31 Jan 2006
01 Dec - 31 Dec 2005
01 Nov - 30 Nov 2005
01 Oct - 31 Oct 2005
01 Sep - 30 Sep 2005
01 Aug - 31 Aug 2005
01 Jul - 31 Jul 2005
01 June - 30 June 2005
01 May - 31 May 2005
01 Apr - 30 Apr 2005
01 Mar - 31 Mar 2005
01 Feb - 29 Feb 2005
01 Jan - 31 Jan 2005
01 Dec - 31 Dec 2004
01 Nov - 30 Nov 2004
01 Oct - 31 Oct 2004
01 Sep - 30 Sep 2004
01 Aug - 31 Aug 2004

Stuff

Powered by Pivot  
XML: RSS feed 
XML: Atom feed 

§ 68000 assembly language

Nostalgia alert.

I've been writing 80386 assembly language for about ten years now, and I've gotten rather used to it -- enough, at least, to write some fairly long assembly language fragments for some obscure video program. 80386 assembly has a lot of goofy syntax and instruction set idiosyncracies, and it's almost the ugliest and nastiest assembly language I know, second only to 8086 real mode. It probably didn't help that I had the joy of learning the latter with the original IBM Macro Assembler 1.00.

I know a couple of other assembly languages, too, such as 6502 -- of which my main abuses can be summed up with BIT $C030 -- and enough MIPS that I can squeak by when reading a PowerPC listing. My favorite assembly language of all time, however, is that of the MC68000. Not only was that the CPU on which I did most of my early major assembly language coding, but the assembly code is very readable, and the instruction set rather flexible.

68000 basics

The 68000 has the rare (unique?) characteristic of having separate address and data registers. There are eight data registers, D0-D7, and eight address registers, A0-A7. A7 is also the stack pointer, SP. This means that 68000 assembly language tends to be easier to follow because you can easily tell which registers hold data and which hold addresses. For example, this is 68000 assembly to compute the sum of an array of words:

    moveq #0, d0
    moveq #0, d1
loop:
    move.w (a0)+, d0
    add.l d0, d1
    dbra d2, loop

It also had an instruction to quickly push or pop a series of registers, which made function prologues and epilogues very compact:

movem.l a0-a4/a6/d0-d3/d5/d7, -(sp)

...and it had both memory-to-memory moves and predecrement/postincrement modes:

copyloop:
    move.l (a0)+, (a1)+
    dbra d0, copyloop

With as many as fifteen registers available for use, the 68000 naturally lends itself to a register passing scheme for function call parameters and keeping most working variables within the register set. I hate writing spill code, so I tended to use most or all of the registers, often to the point of reusing address registers for data or using the upper 16 bits of data registers for temporary storage.

For the most part, the 68000 treats all data and address registers equivalently, so register selection is easy. One hidden trap is that:

move.b d0, -(a7)
move.b (a7)+, d0

...actually transfer words, since byte moves with predecrement or preincrement are special-cased to keep the stack aligned. Of course, if you were a real 68K programmer, you abused this to quickly shift a word by 8 bits.

Development environment

Most of the coding I did was on an Amiga 2000 with a 28MHz cached 68000 (don't ask). A little bit was done to optimize programs written in Lattice C which stubbornly used longwords for ints but most of the 68K I wrote was actually in a language extension for a BASIC variant called AMOS Professional, which is best described as BASIC on steroids. I added statements for things like asynchronous disk I/O, smooth slider UI widgets, nearly instant maze generation, and LFSR noise generation. This might sound insane, but you have to realize that this was a BASIC interpreter that had verbs for disabling interrupts and running code from vertical blank.

A major annoyance of the 68000 is that it is incapable of accessing word or longword data misaligned, i.e. on an odd address. You would want to align variables anyway for performance, but a slow read is still better than having code blow up with an address error when you forget. The Amiga didn't have a way to cleanly kill a task and reclaim its resources; the usual mode of development was to keep dragging the program errors off screen until you ran out of memory and then restart your machine, which was optimized to boot quickly. (This is the same thing you did in Windows 95 when programs kept crashing in the code to dismiss the crash dialog.)

The other annoyance was discovering that the assembler had helpfully encoded a equality branch as beq.l, which is fine except that branch instructions with 32-bit offsets required a 68020.

I only did a little 68000 coding on the Macintosh for a class project, and I hated it, because classic MacOS insists on being able to move your application's data segment (pointed to by A5) during many system calls. This meant you couldn't keep pointers to mutable data unless you relocated them somehow. I ended up littering my assembly with junk like this:

sub.l a5, a2
sub.l a5, a3
jsr randomcall
add.l a5, a3
add.l a5, a2

Besides that little annoyance, during that class I tried to optimize my programs before submission, which probably confused the poor TAs who thought that movea.b d0, a0 was a valid instruction.

I did a little bit of 68000 later on a Palm device, which used a 68328 (Dragonball), an embedded MCPU with a 68K core. Using gas for an assembler was a little annoying because I had to prefix all register references with %, but that was more than made up for by being able to use the C preprocessor.

68000 tricks

Long time ago, when BYTE magazine was still good, they had a green-covered issue on the 68000 which had an article with goofy tricks and tips for writing fast 68000 code. The 68000 is often instruction fetch bottlenecked, so often the goal was to select the fast instructions that ran as close as possible to the 4 clock/instruction limit. The simplest, and most useful trick, was that the straightforward way of clearing a register:

clr.l d0

...was actually two clocks slower than using the move-quick instruction:

moveq #0,d0

Such tricks were the basis of knowing how to abuse the instruction set. Some of my favorites are (forgive me for errors, since I haven't done 68K for a while):

The movem (move multiple) instruction, while usually for saving or restoring registers from the stack, was also the fastest way to move blocks of memory. Using 15 registers, you could nearly reach the limit of the bus. It was significantly faster than the standard copy loop, possibly even on a 68010 with its loop mode.

movep was an obscure instruction to read or write data from an 8-bit device that had been connected to the 68000's 16-bit bus. It would transfer a word or longword on a series of byte locations whose addresses were all even or odd. You could also use this to slightly optimize a series of sparse, unaligned moves. I'm proud to say I broke someone's 68000 emulator with this instruction once, presumably because its use is so rare.

Unsigned clamping (for which I got sworn at by a fellow programmer):

    cmp.w d1, d0
    bcs.b noclamp
    spl d0
    ext.w d0
    and.w d1, d0
noclamp:

Jump tables were surprisingly difficult to do quickly. Unlike the 386, which treats addressing modes in a call or jump instruction the same way as for others, the 68000 uses the effective address of the memory operand instead, which means you can't do a table lookup in the JSR instruction. So I ended up actually making the table a list of branches... with some optimized cases:

    add.w d0, d0
    add.w d0, d0
    jmp jumptable(pc, d0.w)

jumptable: bra.w target1 moveq #17, d2 bra.b target2 moveq #-19, d2 bra.b target2 bra.b target3 nop

Comments

Comments posted:


Hi,

AMOS is still there you can download it and run it on the Amiga emulator. I used to-do a lot of AMOS programming on my Amiga 500 and it was so sweet I think it's the 1st language that had 3D commands build into it.

Shay Erlichmen - 21 01 06 - 12:37


I loved the female voice greeting most - "Amos Professional"
Nice basic with the ability to program the copper yourself. I still have a game I coded with it, though it does not work right under WINUAE.
Seems like the Fancois Lionet did some ugly hacking with his basic...

Murmel - 22 01 06 - 08:02


Shay:
I'm pretty sure AMOS 3D was an add-on to the base interpreter. AMOSPro definitely didn't have the 3D commands.

Murmel:
Strange, I actually remember AMOSPro not hacking the hardware enough -- being able to position screens at finer than 16 pixel increments would have been nice. Did you try using AmigaOS 2.0 and turning on all the emulation options to max accuracy?

Phaeron - 22 01 06 - 17:33


AmosPro had an addon language called AMAL = Amos Animation Language. The only reason I ever tried to code games with animation and sound...

Phaeron:
I only know that the visiblity-check does not work anymore.
It tests line-of-sight by checking pixel-colorindexes on a line from player to enemy. I only have the executable and no more source, so I can't really tell what's going wrong.

Murmel - 23 01 06 - 14:47


Yes, I meant as an addon, still prety nifty for those days...
BTW you can download the source code of AMOS. (100% pure 68K ASM)

Shay Erlichmen - 23 01 06 - 15:01


Yep, I'm with you. I still do 68000 on my Atari STE and Falcon (purchased recently). 68000 asm rocks :)

BTW the movep instruction isn't so rare as you might think at first. Demo coders have abused this instruction for lots of stuff. Have a look, for example at http://alive.atari.org/alive8/c2p.php and you'll get my point

George Nakos - 24 01 06 - 03:57


I doubt you could find a single movep instruction in the vast majority of PalmOS applications, and I'd probably use movep everywhere too if I still had to put up with bitplanes. :)

Phaeron - 24 01 06 - 04:43


I had programmed a lot with the 6800 (has two 8-bit accumulators and one 16-bit index register). My abuse is branching into the middle of 2 byte instructions :)

meanie - 27 01 06 - 18:08


"almost the ugliest and nastiest assembly language I know, second only to 8086 real mode."

Hey now -- don't trod on my beloved 8086 :-) The 8086 has a metric crapload of idosyncracy, I'll agree, but some of it was incredibly useful. XLAT remains one of my favorite hammers of that platform, along with the repeated string instructions (REP STOS, REP MOVS, etc.).

Jim Leonard (link) - 03 03 06 - 15:56


Are you serious that the postincrement actually assembles to a single instruction? That's so cool.

Is moveq #0,d0 actually faster than XORing d0 with itself, or is that just a quirk of x86? *glances at instruction set*... Hey... I don't even -see- a XOR here. What's up with that?

Publius - 17 11 06 - 10:32


Exclusive-or is EOR in 68000 assembly, not XOR.

If you only need Dn.W zeroed, then both MOVEQ #0, Dn and EOR.W Dn, Dn are both 4 clocks. However, if you need all 32 bits zeroed (Dn.L), then it's faster to use MOVEQ, because EOR.L Dn,Dn is 6 clocks. (or is it 8?)

Phaeron - 17 11 06 - 23:24


As I remember,
Sub.l a0,a0 is faster than Move.l #0,d0
But Moveq #0,d0 is faster than Sub.l #0,d0.
I'm not sure Eor.l is faster than Move.l #0,d0 or Moveq #0,d0

Very nice page, thank you :)

Endo - 25 01 08 - 04:34


The 68000 only has a 16-bit ALU, so sub.l and eor.l are both slower than moveq for data registers. move.l #0 is even slower, but that's because of instruction fetch being the bottleneck.

Phaeron - 26 01 08 - 16:22


Hi, All,

I have old version of binary(BIN) code in my 68K flash without soruce code but I have its reviced code(Do not know how many changes). I have to add some new function to this old BIN code by add new software build and link those two pioeces of program I want to let BIN porgram to handle ISR(Interrupt Service Routine) to point my new code Whether I can modify the ISR Jumping pointer of old BIN to my new function code??? and then redownload to Flash?

Fred Gao - 13 06 08 - 19:06


"classic MacOS insists on being able to move your application's data segment (pointed to by A5) during many system calls."
Should not happen, otherwise almost all Mac programs would crash. Did you forget to lock the handle to the memory that A5 pointed into?

Yuhong Bao - 30 07 08 - 20:14


"application's data segment (pointed to by A5)"
The right name for this is the A5 world.

Yuhong Bao - 31 07 08 - 01:39


There are still loads of us writing code for the Amiga and it's various OS'es (old and new). I myself love AMOS Basic and am currently writing my own extension (Basic commands) for it, hence why I am here on your ASM page :)
For fellow AMOS fans there is still a fairly active Yahoo group (AMOS-LIST) and website (AMOS Factory) which can be found by searching on Amiga.org or your favourite search engine (maybe).

Andrew D. Burton - 13 08 08 - 18:20


I love the 68K. They did so many things right from the get-go
#1 values move between operands left to right
#2 flat 32-bit addressing (no segments:offsets garbage). Yes I know the original 68K worked with a 16-bit address bus.
#3 Motorola never assumed that nobody would ever need more than 640K RAM.
#4 "move.l (a0)+,(a1)+" looks more eloquent than "rep movsd"
#5 orthogonal instruction set
#6 "move.l (a0),d0" looks more eloquent than "mov edx, DWORD [edx]"

Al Leitch - 07 01 12 - 12:43

Comment form


Please keep comments on-topic for this entry. If you have unrelated comments about VirtualDub, the forum is a better place to post them.
Name:  
Remember personal info?

Email (Optional):
Your email address is only revealed to the blog owner and is not shown to the public.
URL (Optional):
Comment: /

An authentication dialog may appear when you click Post Comment. Simply type in "post" as the user and "now" as the password. I have had to do this to stop automated comment spam.



Small print: All html tags except <b> and <i> will be removed from your comment. You can make links by just typing the url or mail-address.