# Motorola 68000 Assembly Language ## How-Tos ### Set up a 68K LSP for NeoVim with CoC If you're using CoC with NeoVim, you can enable a 68k Language Server Protocol server running in Node: https://github.com/grahambates/m68k-lsp ``` # install the language server npm i -g m68k-lsp-server # configure CoC to use the LSP :CocConfig # add to the JSON file: "languageserver": { "m68k": { "command": "m68k-lsp-server", "args": ["--stdio"], "filetypes": ["asm"], "rootPatterns": ["*.s"] } # if you're using ALE as well as CoC, ALE may complain, like a lot about the file. # I think ALE thinks it's x86 assembler. Disable ALE on that buffer: :ALEDisableBuffer ``` Now you can get docstrings and instruction reference: {{ :m68k:development:screenshot_20240430_215439.png |}} {{ :m68k:development:screenshot_20240430_215640.png |}} ### Set memory breakpoints for debugging within WinUAE/FS-UAE You can set a "breakpoint" in code by triggering a memory write to a safe area of memory (https://eab.abime.net/showpost.php?p=899516&postcount=6). This will trigger the debugger at that point in code. In your code: ```asm ; code before breakpoint CLR.W $100 ; write two zero bytes to $100 ; code you want to verify ``` Then, in WinUAE/FS-UAE, set a memory watchpoint. This creates watchpoint 0, looking at $100, waiting for two bytes to be written. In your user code (at least in AMOS extensions), nothing else will be writing here. ``` w 0 100 2 ``` Start the code executing again with `g` and UAE will break when it hits your `CLR.W`. For actually debugging: * `z` for stepping forward one instruction Remove the memory watchpoint with `w 0`. This also works in C: ```c int main(void) { static volatile short *debug = (volatile short*) 0x100; int a; a = 1; *(debug) = 0; a = 2; return 0; } ``` ### Debugging checklist * Ensure things are constants that should be constants! ```asm CUSTOM EQU $DFF000 ; bad, better reboot the Amiga MOVE.L CUSTOM,A0 MOVE.L CopperlistPointer,cop1lc(A0) ; good MOVE.L #CUSTOM,A0 MOVE.L CopperlistPointer,cop1lc(A0) ``` * Watch for address register usage when looking at code from other sources! ```asm MOVE.W #$2c21,diwstrt(A0) MOVE.W #$2cc1,diwstop(A0) MOVE.W #$0038,ddfstrt(A1) ; uh oh MOVE.W #$00d0,ddfstop(A1) ``` ## References * http://wpage.unina.it/rcanonic/didattica/ce1/docs/68000.pdf * https://github.com/prb28/m68k-instructions-documentation - This includes information on Amiga hardware registers * http://68k.hax.com/ * https://amigasourcecodepreservation.gitlab.io/mc680x0-reference/ * https://eab.abime.net/showthread.php?t=116417 ### Addressing Modes * https://www.thedigitalcatonline.com/blog/2019/03/04/motorola-68000-addressing-modes/ ### Registers * https://www.cs.mcgill.ca/~cs573/fall2002/notes/lec273/lecture9/ ## Try it out! * https://asm-editor.specy.app/ ## Processor versions ### 68020 * https://wiki.preterhuman.net/Optimizations_for_the_68020 ## Instructions ### MOVE The default size seems to be word, so `MOVE` and `MOVE.W` seem to be equivalent. ### ASL and ROL If you want to shift a high bit from one thing onto the low bit of another, `ASL`, will shift off the top bit into the carry flag without wrapping it back around. Unlike 6502 assembler, `ROL` will not use the carry flag. You have to do that yourself: ```asm MOVE.L #$ffffffff,D0 MOVE.L #$1,D1 ASL.L #1,D0 SCS D2 AND.L #$1,D2 ROL.L #1,D1 ; D1 is now 2 ADD.L D2,D1 ; D1 is now 3 ``` ## Algorithms ### Divide two long numbers and get two long numbers back A bit more work than `DIVU` but it allows you to work with longs. Based on finally figuring out [multiplying and dividing on the 6502](https://www.llx.com/Neil/a2/mult.html). ```asm START: MOVE.L #31,D4 loop: ASL.L #1,number SCS D2 AND.L #1,D2 ROL.L #1,remainder ADD.L D2,remainder MOVE.L remainder,D3 SUB.L divisor,D3 BMI keep_going ADD.L #1,number MOVE.L D3,remainder keep_going: DBRA D4, loop move.l number,d4 ; 2 move.l remainder,d5 ; 500000 RTS number: dc.l 2500000 divisor: dc.l 1000000 remainder: dc.l 0 ``` ### DBcc Remember that `DBcc` checks for the condition both before and after the decrement! This is a very likely source of off-by-one errors if you get it wrong, so be mindful! ## Macros * If you want to use a macro over and over and have labels that come along with it, use `\@` in the macro code to expand to `_`. * If you're accepting a register as a parameter, make sure you're not using that same register in the code. At that point, a subroutine might be better. ## Alignment * If something needs to be long-word aligned, used `CNOP 0,4`.