68000 programming.
Anyone here know anything about this?
My question is: how does the programmer know how long hisinstructions are? Do determine the operands to branch commands etc...
My question is: how does the programmer know how long hisinstructions are? Do determine the operands to branch commands etc...
Post edited by wilsonsamm on
Comments
https://twitter.com/bobsstuffgames
https://www.facebook.com/bobs.stuff
http://www.bobs-stuff.co.uk
http://eab.abime.net/
It's the Amiga eqivalent of WoSF.
The command size will vary depending on number and type of arguments, much like other CPUs. An assembler will sort it out for you if you label the code you want to jump to. I'd strongly recommend against trying to code it by hand in hex...
I frequently use 680x0 asm and have the CPU manuals, so if there's something specific you wanted to know or there's a short piece of code you needed to create and you don't have an assembler, let me know and I'll try to help.
But can someone tell me what's wrong with these instructions? I keep getting an "invalid adressing mode" error with them...
andi.w $00FF,a1
bclr $8,a1
ori.w $FF80,a2
You can't do logical operations on address registers. So and, or, bit ops, etc are not allowed. Do them on data registers instead. You can move to and from address registers, as well as add and subtract, with some limitations, but condition codes are (usually) not affected when an address register is the destination.
Also, for immediate (constant) numbers, prefix the argument with "#". A number without the # means an absolute memory address location.
i.e:
andi.w #$00ff,d0 ;AND $ff with d0.
and.w $00ff,d0 ;AND contents of location $ff with d0.
(Also, most assemblers will convert an "and" into and "andi" as required.)
IIRC indirect access and direct (and indirect) access to Data registers only are allowed.
I'm finding it a little bothersome that there are limitations like what register you can use with what opcode. And people say it's orthogonal? pffft. not like the pdp-11 where you can do things like
add 3,r6 #adding 3 to the stack pointer, like pushing three zeroes, or putting back whatever was there before
or
push (r7) #push current opcode onto stack (r7 is the program counter) using indirect adressing
but still there's some amazing things being done with the 68k. maybe someday I will be able to harness this power.
Yeah, I think orthogonal might be stretching the truth for 68K. :-)
Like most CISC processors there are groups of registers that are optimised to perform certain functions. It certainly isn't as free-form as something like an ARM. On the other hand, it is fairly free (compared to most RISC cores) about whether the data is in memory or a register, so you "save" register use like that. (e.g. memory-to-memory transfers do not require an intermediate register.)
Well, if it makes you any happier, a7 is the stack pointer and you can do what you like with it in the same way as any other address register, although you'd rarely want to as it ruins the stacking.
The 68K equivalents of push and pop are:
push: move xxx,-(a7)
pop: move (a7)+,xxx
Check out movem for multiple registers:
movem.l d0-d4/a3,-(a7) ;push d0,d1,d2,d3,d4,a3
You can also "pea" - push effective address:
pea blob ;push the address "blob" onto the stack.
pea -$14(a0) ;push a0-$14 onto the stack.
There's also link and unlk to build stack frames, not that they're often used.
The 68K has some quite useful addressing modes, like:
move.l $34(a0,d0.l),(a1)+
-- Move the contents of memory location a0 + d0 + $34 to the memory location pointed to by a1 and increment a1 by the size of the data.
On 68020 and above, you can scale the data register index:
move...(a0,d0.l*4),...
So you can index directly over byte, word, longs, or quads without having to pre-scale the index.
With most addressing forms, you can replace an address register with pc, which can be useful for pc-relative relocatable code.
The 68K seems to have attained a reputation for being one of the nicest instruction sets to actually program with (flexible, easy mnemonics, simple model, etc). It doesn't necessarily win any accolades for performance or beauty. ;-)
So how do interrupts work on the 68k? the pdp-8 always saves the PC in location 0 and starts executing from location 1, where you can have a polling loop or whatever to deal with it. then the last instruction in the interrupt handling routine would be an indirect jump thrô location 0, taking the computer back to whatever it was doing before. But how does the 68k deal with it? And there are 8 levels of interrupt?
The 68K has 8 interrupts and 8 interrupt levels. When one of the 8 interrupts is triggered, a vector address for that interrupt is fetched from a specific address in low memory. (On 68020 and above, the location of these addresses can change via a Vector Base Address register.) The 68K stacks an "exception" stack frame, which saves status registers and return address on the supervisor stack - not the same as the a7 user stack. (Assemblers use usp and ssp to differentiate when required.) It then jumps to the address fetched from the vector. You can return and reinstate status with an RTE instruction.
Each interrupt routine has a priority equal to it's number. So "int 4" can interrupt "int 3" processing, but "int 2" cannot. Int 8 is actually an NMI and cannot be prevented.
Interrupts on 68K are treated as "exceptions" along with illegal instructions, divide by zero, bus faults, traps, etc. Each has a vector for a support routine. Most programs run in user mode in which certain instructions are not allowed. When an exception is taken, the processor is in supervisor mode which has no restrictions. Nominally it is for OS vs. user programs and the security thereof. The 68K boots up in supervisor mode.
I think you mean:
move.w (firstnumber),d0
add.w (secondnumber),d0
and.w #$0fff,d0 ;result in d0
If you need to determine that is has overflowed, you'd need to mask and test for it explicitly, or forget about 12-bit masking and just shift all numbers left by 4 bits and use the carry flag.
d0 holds the opcode we're currently emulating. Now the cmpi checks if it is indeed ISZ we're meant to be doing, and skips the entire routine if not. I do this with every opcode the pdp-8 has.
INTER is the routine which (eventually) checks if an interrupt has occurred.
AC is the emulated accumulator.
well I ran my new code for the first time today. I found it does nothing but put a few nonsense bytes into registers d1, d2, d3. debug time, methinks.
I found out what was going wrong with my code: the command I use for fetching my opcode, posted below, only fetches a byte at a time, when I really need two.
doesn't .w signify a word - 16 bits?
A 68000 will throw a bus error if a0 isn't word-aligned when you try that. 68020 up can fetch misaligned data.
Edit:
Bear in mind also that 68K is big-endian. The lowest-address byte will be the top 8 bits of the 16-bit word. Opposite to our beloved Speccy Z80 CPU.
I'm not too sure what you're up to with the ORI instructions there. Particularly:
"SKIPS ori #$F000,d1 twelve leftmost bits in AC will always be 0000 in this case."
The leftmost 4 bits of d1 become 1111 if you do that, and the rightmost 12 are left alone. :-?
You also appear to be operating on the data in d2 at one moment, then d1 the next. Apologies if I haven't understood what you're trying to do.
I'd have done something like:
addq.w #1,d1 move.w d1,d2 and.w #$0fff,d1 cmp.w d1,d2 beq NoSkip Skip: ....And since you're only ever adding one, maximum, you can get away with:
addq.w #1,d1 and.w #$0fff,d1 bne NoSkip Skip: ....As I say, I'm sorry I don't understand what all the results are, and in which registers, that you want here.
BTW, most emulators wouldn't go through a chain of cmpi/bne to decide what instruction to emulate. You'd use a jump table. Or is your cmpi some sort of error check?
I'm posting the source here, incase anyone's interested. At the beginning there's a small pdp-8 routine, which is then run by the emulator which starts at 1000. This routine does this: the first few instructions run ok, but the third fails because of a mistake in the routine for handling indirect addressing. Can someone see what the mistake is?
When the adress is direct, there's a command which says "asl.w #$1,d2". this basically doubles the adress, so that this number is the adress in bytes, like the 68k does it, not words as does the PDP-8. This of course, has to be done again if it's indirect adressing as well.
Glad you're getting confident enough with 68K to find bugs in your code. :-)