├── .gitignore ├── LICENSE ├── README.md ├── blob.asm ├── blob └── DebuggerBlob.asm ├── build.sh ├── img ├── address-error.png ├── disassembler-test.png └── div-by-zero.png └── src ├── Debugger.asm ├── DebuggerMap.bin ├── Disassembler.asm ├── Terminal.asm ├── TerminalFont.bin ├── TerminalPal.bin ├── skcompat.asm └── vdp-macros.asm /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.7z 3 | blob/*.bin 4 | blob.bin 5 | blob.h 6 | blob.lst 7 | blob.p 8 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD Zero Clause License 2 | 3 | Website: https://spdx.org/licenses/0BSD.html 4 | 5 | tl;drLegal: https://tldrlegal.com/license/bsd-0-clause-license 6 | 7 | Permission to use, copy, modify, and/or distribute this software for any 8 | purpose with or without fee is hereby granted. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16 | OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Readme 2 | 3 | ## License 4 | 5 | This uses a bsd 0-Clause License (0BSD). The TL;DR version is [here](https://tldrlegal.com/license/bsd-0-clause-license). 6 | 7 | Basically, you can use however you want, and you don't have to add credits, licenses, or anything to your hack. 8 | 9 | I only ask for the courtesy of giving some credit if you use it, but you are not forced to do it. 10 | 11 | ## About 12 | 13 | This is a general error debugging screen for the Sega Genesis witht he following features: 14 | 15 | - integrated disassembler, which is logically separated from the drawing code and from the debugger screen; 16 | - the code almost fully position-independent, meaning a binary blob can be made; 17 | - recovers more information from branches and many jumps, including dbCC opcodes; 18 | - brand new font with ASCII encoding, so you can roll your own; 19 | - handles all errors that can happen on a 68k, even if they cannot happen on a real Sega Genesis (such as bus errors); 20 | - has code that will make macro haters want to stay away. 21 | 22 | Note: If you use asm68k, you will not be able to build this. I use macros and functions to make the code more maintainable and readable, and asm68k sucks. Badly. And I am not saying this only in regards to macros -- I used to think it was only slightly worse than AS, but now I stand corrected: while I was working on a binary blob to support asm68k, I ran into a serious bug in it that caused it to misassemble the code generating a bad binary. If you use asm68k, you are on your own. 23 | 24 | There is another warning I must give: I won't give support for anything but the Sonic Git disassemblies, so if you insist on using outdated disassemblies, you are on your own. 25 | 26 | With those out of the way: first, let me explain how you will go about generating a listing file. In the previous version, I talked about map files; listing files are much better because they are generated much faster; the S2 and S&K disassemblies disable it by default, though. In S2, you have to edit 3 places: one in s2.macrosetup.asm and the rest in s2.asm; so: 27 | in s2.macrosetup.asm: find this: 28 | 29 | ```m68k 30 | listing off 31 | ``` 32 | 33 | and change it to this: 34 | 35 | ```m68k 36 | listing purecode 37 | ``` 38 | 39 | You can also use "on" or "noskipped" instead of "purecode", but I recommend using the latter. So now over to s2.asm, do the same twice. Now whenever you build your hack, a s2.lst file will also be generated. 40 | 41 | For S&K, you have to do the same once in sonic3k.macrosetup.asm, then in sonic3k.asm find this: 42 | 43 | ```m68k 44 | Z80_Snd_Driver: include "Sound/Z80 Sound Driver.asm" 45 | ``` 46 | 47 | and change it to this: 48 | 49 | ```m68k 50 | Z80_Snd_Driver: include "Sound/Z80 Sound Driver.asm" 51 | listing purecode 52 | ``` 53 | 54 | Now that you are armed with listing files, time to hook up the new debugger! So go on ahead and grab it here. There are seven files in this archive; only 6 are needed for S2, the seventh is for compatibility with the S&K disassembly. Extract these to your hack's main folder. You can move these around, as long as you edit the includes in the relevant files. You want to add these includes: 55 | For S2: 56 | 57 | ```m68k 58 | include "_inc/Debugger.asm" 59 | ``` 60 | 61 | For S&K: 62 | 63 | ```m68k 64 | include "skcompat.asm" 65 | include "Debugger.asm" 66 | ``` 67 | 68 | These are enough to get everything working; he other files are pulled in automatically. Now the debugger is being assembled inside your hack; you want to setup the interrupt vector table to make use of it: 69 | 70 | In S2: find ";Vectors:" and replace the whole table with this: 71 | 72 | ```m68k 73 | ;Vectors: 74 | dc.l System_Stack , EntryPoint , BusError , AddressError ; 4 75 | dc.l IllegalInstrError , ZeroDivideError, CHKExceptionError, TRAPVError ; 8 76 | dc.l PrivilegeViolation, TraceError , LineAEmulation , LineFEmulation ; 12 77 | dc.l ErrorTrap , ErrorTrap , ErrorTrap , ErrorTrap ; 16 78 | dc.l ErrorTrap , ErrorTrap , ErrorTrap , ErrorTrap ; 20 79 | dc.l ErrorTrap , ErrorTrap , ErrorTrap , ErrorTrap ; 24 80 | dc.l SpuriousException , ErrorTrap , ErrorTrap , ErrorTrap ; 28 81 | dc.l H_Int , ErrorTrap , V_Int , ErrorTrap ; 32 82 | dc.l TrapVector , TrapVector , TrapVector , TrapVector ; 36 83 | dc.l TrapVector , TrapVector , TrapVector , TrapVector ; 40 84 | dc.l TrapVector , TrapVector , TrapVector , TrapVector ; 44 85 | dc.l TrapVector , TrapVector , TrapVector , TrapVector ; 48 86 | dc.l ErrorTrap , ErrorTrap , ErrorTrap , ErrorTrap ; 52 87 | dc.l ErrorTrap , ErrorTrap , ErrorTrap , ErrorTrap ; 56 88 | dc.l ErrorTrap , ErrorTrap , ErrorTrap , ErrorTrap ; 60 89 | dc.l ErrorTrap , ErrorTrap , ErrorTrap , ErrorTrap ; 64 90 | ``` 91 | 92 | In S&K: find "Vectors:" and replace the whole table with this: 93 | 94 | ```m68k 95 | Vectors: 96 | dc.l Vectors , EntryPoint , BusError , AddressError ; 4 97 | dc.l IllegalInstrError , ZeroDivideError, CHKExceptionError, TRAPVError ; 8 98 | dc.l PrivilegeViolation, TraceError , LineAEmulation , LineFEmulation ; 12 99 | dc.l ErrorTrap , ErrorTrap , ErrorTrap , ErrorTrap ; 16 100 | dc.l ErrorTrap , ErrorTrap , ErrorTrap , ErrorTrap ; 20 101 | dc.l ErrorTrap , ErrorTrap , ErrorTrap , ErrorTrap ; 24 102 | dc.l SpuriousException , ErrorTrap , ErrorTrap , ErrorTrap ; 28 103 | dc.l H_int_jump , ErrorTrap , V_int_jump , ErrorTrap ; 32 104 | dc.l TrapVector , TrapVector , TrapVector , TrapVector ; 36 105 | dc.l TrapVector , TrapVector , TrapVector , TrapVector ; 40 106 | dc.l TrapVector , TrapVector , TrapVector , TrapVector ; 44 107 | dc.l TrapVector , TrapVector , TrapVector , TrapVector ; 48 108 | dc.l ErrorTrap , ErrorTrap , ErrorTrap , ErrorTrap ; 52 109 | dc.l ErrorTrap , ErrorTrap , ErrorTrap , ErrorTrap ; 56 110 | dc.l ErrorTrap , ErrorTrap , ErrorTrap , ErrorTrap ; 60 111 | dc.l ErrorTrap , ErrorTrap , ErrorTrap , ErrorTrap ; 64 112 | ``` 113 | 114 | If you try to assemble and you get errors, it is because you are using an older version of AS; get the new version and replace the ones you have. The Git repositories for the Community disassemblies already have this version, by the way. 115 | 116 | Now you just need to trigger an error to see it in action; but if you do that, you will notice that your name and e-mail are wrong; so open "Debugger.asm" and find the following lines: 117 | 118 | ```m68k 119 | HackerName: vtstring WHITE,"Your name" 120 | EMailmsg: vtstring BLUE ,"your.email@server.domain" 121 | ``` 122 | 123 | Except that the e-mail will not be in yellow and underlined, that is just the forum software being an ass. Anyway, supported colors are WHITE, RED, GREEN and BLUE; you can edit the text at will, but your name should not exceed 11 characters -- this is all the space available in the current setup. As the comment near this says, you can use any of the characters int he ASCII set. 124 | 125 | Another thing to do is edit the value of the "Revision" constant in the same file -- this number can be used to uniquely identify the version in which the error happened, so you will know if you already fixed the error or not. 126 | 127 | When all is said and done, this is what you will see (using two manufactured examples): 128 | 129 | ![Address error sample](img/address-error.png) ![Division by zero sample](img/div-by-zero.png) 130 | 131 | As a bonus: since the code is logically separate, the disassembler can be used on its own; here is an example of a minimal disassembler that can be build using it: 132 | 133 | ![Disassembler sample](img/disassembler-test.png) 134 | 135 | This was done with this code: 136 | 137 | ```m68k 138 | DisassemblyTest: 139 | bsr.w InitTerminal 140 | lea DrawTerminal(pc),a3 141 | tellp.l -(sp) ; Save end of buffer 142 | setcursor DisassemblyAlign,0 143 | 144 | .instr_loop: shared .instr_loop 145 | move.l (sp),d0 ; Get end of screen buffer 146 | vtchkp.l d0 ; Is this after current position? 147 | blo.w .draw_terminal ; Branch if not 148 | tellp.l -(sp) ; Save start of line 149 | move.w (a3)+,d5 ; Read instruction 150 | lea RewindStub(pc),a4 ; Pointer to rts 151 | bsr.w Disassemble_Opcode 152 | movea.l a2,a3 ; PC for next instruction 153 | tellp.l d0 ; Save current position on buffer 154 | seekset (sp)+ ; Seek back to old position 155 | 156 | .next_line: 157 | seekp.l nCols ; Advance it by one line 158 | vtchkp.l d0 ; Is this after current position on buffer? 159 | bhs.s .next_line ; Branch if not 160 | bra.s .instr_loop 161 | ;--------------------------------------------------------------------------------------- 162 | .draw_terminal: 163 | addq.w #4,sp ; Pop end of screen buffer from stack 164 | bsr.w DrawTerminal 165 | 166 | .error_trap: 167 | nop 168 | nop 169 | bra.s .error_trap 170 | ; =========================================================================== 171 | ``` 172 | -------------------------------------------------------------------------------- /blob.asm: -------------------------------------------------------------------------------- 1 | ; =========================================================================== 2 | ; Copyright (C) 2011-2018 by flamewing 3 | ; 4 | ; Permission to use, copy, modify, and/or distribute this software for any 5 | ; purpose with or without fee is hereby granted. 6 | ; 7 | ; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | ; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | ; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | ; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 13 | ; OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | ; =========================================================================== 15 | CPU 68000 16 | supmode on ; We don't need warnings about privileged instructions 17 | listing purecode ; Want listing file, but only the final code in expanded macros 18 | 19 | paddingSoFar set 0 20 | 21 | ; 128 = 80h = z80, 32988 = 80DCh = z80unDoC 22 | notZ80 function cpu,(cpu<>128)&&(cpu<>32988) 23 | 24 | ; define the even pseudo-instruction 25 | even macro 26 | if notZ80(MOMCPU) 27 | if (*)&1 28 | paddingSoFar set paddingSoFar+1 29 | dc.b 0 ;ds.b 1 30 | endif 31 | else 32 | if ($)&1 33 | db 0 34 | endif 35 | endif 36 | endm 37 | 38 | ; makes a VDP address difference 39 | vdpCommDelta function addr,((addr&$3FFF)<<16)|((addr&$C000)>>14) 40 | 41 | ; makes a VDP command 42 | vdpComm function addr,type,rwd,(((type&rwd)&3)<<30)|((addr&$3FFF)<<16)|(((type&rwd)&$FC)<<2)|((addr&$C000)>>14) 43 | 44 | ; values for the type argument 45 | VRAM = %100001 46 | CRAM = %101011 47 | VSRAM = %100101 48 | 49 | ; values for the rwd argument 50 | READ = %001100 51 | WRITE = %000111 52 | DMA = %100111 53 | 54 | ; tells the VDP to copy a region of 68k memory to VRAM or CRAM or VSRAM 55 | dma68kToVDP macro source,dest,length,type 56 | lea (VDP_control_port).l,a5 57 | move.l #(($9400|((((length)>>1)&$FF00)>>8))<<16)|($9300|(((length)>>1)&$FF)),(a5) 58 | if source>=0 59 | lea source(pc),a1 60 | move.l a1,d1 61 | move.w #$9500,d0 ; command to specify source address & $0001FE 62 | lsr.l #1,d1 63 | move.b d1,d0 64 | move.w d0,(a5) ; Send to VDP 65 | 66 | move.w #$9600,d0 ; command to specify source address & $01FE00 67 | lsr.w #8,d1 68 | move.b d1,d0 69 | move.w d0,(a5) ; Send to VDP 70 | 71 | move.w #$9700,d0 ; command to specify source address & $FE0000 72 | swap d1 73 | move.b d1,d0 74 | move.w d0,(a5) ; Send to VDP 75 | else 76 | move.l #(($9600|((((source)>>1)&$FF00)>>8))<<16)|($9500|(((source)>>1)&$FF)),(a5) 77 | move.w #$9700|(((((source)>>1)&$FF0000)>>16)&$7F),(a5) 78 | endif 79 | move.w #((vdpComm(dest,type,DMA)>>16)&$FFFF),(a5) 80 | move.w #(vdpComm(dest,type,DMA)&$FFFF),(a5) 81 | endm 82 | 83 | ; tells the VDP to fill a region of VRAM with a certain byte 84 | dmaFillVRAM macro byte,addr,length 85 | lea (VDP_control_port).l,a5 86 | move.w #$8F01,(a5) ; VRAM pointer increment: $0001 87 | move.l #(($9400|((((length)-1)&$FF00)>>8))<<16)|($9300|(((length)-1)&$FF)),(a5) ; DMA length ... 88 | move.w #$9780,(a5) ; VRAM fill 89 | move.l #$40000080|(((addr)&$3FFF)<<16)|(((addr)&$C000)>>14),(a5) ; Start at ... 90 | move.w #(byte)<<8,(VDP_data_port).l ; Fill with byte 91 | .loop: move.w (a5),d1 92 | btst #1,d1 93 | bne.s .loop ; busy loop until the VDP is finished filling... 94 | move.w #$8F02,(a5) ; VRAM pointer increment: $0002 95 | endm 96 | 97 | ; calculates initial loop counter value for a dbf loop 98 | ; that writes n bytes total at 4 bytes per iteration 99 | bytesToLcnt function n,n>>2-1 100 | 101 | ; tells the Z80 to stop, and waits for it to finish stopping (acquire bus) 102 | stopZ80 macro 103 | move.w #$100,(Z80_Bus_Request).l ; stop the Z80 104 | .loop: btst #0,(Z80_Bus_Request).l 105 | bne.s .loop ; loop until it says it's stopped 106 | endm 107 | 108 | ; tells the Z80 to start again 109 | startZ80 macro 110 | move.w #0,(Z80_Bus_Request).l ; start the Z80 111 | endm 112 | 113 | ; A few constants 114 | palette_line_0 = (0<<13) 115 | palette_line_1 = (1<<13) 116 | palette_line_2 = (2<<13) 117 | palette_line_3 = (3<<13) 118 | tile_mask = $07FF 119 | 120 | ramaddr function x,-(-x)&$FFFFFFFF 121 | 122 | ; Remapped RAM locations 123 | Chunk_Table = ramaddr($FFFF0000) 124 | System_Stack = ramaddr($FFFFFE00) 125 | 126 | VDP_data_port = $C00000 ; (8=r/w, 16=r/w) 127 | VDP_control_port = $C00004 ; (8=r/w, 16=r/w) 128 | 129 | Z80_Bus_Request = $A11100 130 | Z80_Reset = $A11200 131 | 132 | ; VRAM constants 133 | ArtTile_VRAM_Start = $0000 134 | 135 | ; simplifying macros and functions 136 | 137 | ; macros to convert from tile index to art tiles, block mapping or VRAM address. 138 | make_art_tile function addr,pal,pri,((pri&1)<<15)|((pal&3)<<13)|(addr&tile_mask) 139 | tiles_to_bytes function addr,((addr&$7FF)<<5) 140 | 141 | ; macro to declare an offset table 142 | offsetTable macro {INTLABEL} 143 | current_offset_table := __LABEL__ 144 | __LABEL__ label * 145 | endm 146 | 147 | ; macro to declare an entry in an offset table 148 | offsetTableEntry macro ptr 149 | dc.ATTRIBUTE ptr-current_offset_table 150 | endm 151 | 152 | ROMEndLoc = $1A4 153 | 154 | debugger_blob = 1 155 | 156 | ; Remapped function names 157 | KosDec: 158 | dc.w $4EF9, $0BAD, $F00D 159 | EniDec: 160 | dc.w $4EF9, $DEAD, $BEEF 161 | 162 | include "Debugger.asm" 163 | 164 | -------------------------------------------------------------------------------- /blob/DebuggerBlob.asm: -------------------------------------------------------------------------------- 1 | ; =========================================================================== 2 | ; Copyright (C) 2011-2018 by flamewing 3 | ; 4 | ; Permission to use, copy, modify, and/or distribute this software for any 5 | ; purpose with or without fee is hereby granted. 6 | ; 7 | ; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | ; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | ; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | ; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 13 | ; OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | ; =========================================================================== 15 | ; For all the "unhandled" vectors. 16 | ErrorTrap: 17 | nop 18 | nop 19 | bra.s ErrorTrap 20 | 21 | ; These get called from the binary blob. Do not edit them, or move them 22 | ; relative to the binary blobs below. 23 | jmp (KosDec).l 24 | jmp (EniDec).l 25 | 26 | ; This is the terminal code and graphics, plus the disassembler and the plane 27 | ; mappings for the debugger. 28 | incbin "_debugger/Part1.bin" 29 | 30 | WHITE EQU 0<<13 31 | BLUE EQU 1<<13 32 | RED EQU 2<<13 33 | GREEN EQU 3<<13 34 | ; Strings are word arrays: length followed by characters. You can change the 35 | ; length, but do NOT change the number of characters! The wasted space is the 36 | ; price to pay for a binary blob... 37 | ; The high byte of each word used for a character is the palette line to use: 38 | HackerName: 39 | dc.w 11 40 | dc.w WHITE|'Y', WHITE|'o', WHITE|'u', WHITE|'r', WHITE|' ', WHITE|'N' 41 | dc.w WHITE|'a', WHITE|'m', WHITE|'e', WHITE|' ', WHITE|' ' 42 | even 43 | EMailmsg: 44 | dc.w 33 45 | dc.w BLUE|'y', BLUE|'o', BLUE|'u', BLUE|'r', BLUE|'.', BLUE|'e' 46 | dc.w BLUE|'m', BLUE|'a', BLUE|'i', BLUE|'l', BLUE|'@', BLUE|'s' 47 | dc.w BLUE|'e', BLUE|'r', BLUE|'v', BLUE|'e', BLUE|'r', BLUE|'.' 48 | dc.w BLUE|'d', BLUE|'o', BLUE|'m', BLUE|'a', BLUE|'i', BLUE|'n' 49 | dc.w BLUE|' ', BLUE|' ', BLUE|' ', BLUE|' ', BLUE|' ', BLUE|' ' 50 | dc.w BLUE|' ', BLUE|' ', BLUE|' ' 51 | even 52 | 53 | ; Do not move or add padding between the code that follows. The debugger is 54 | ; split into these many parts because asm68k sucks. 55 | BusErrorMsg: 56 | incbin "_debugger/Part2.bin" 57 | 58 | BusError: 59 | incbin "_debugger/Part3.bin" 60 | 61 | AddressError: 62 | incbin "_debugger/Part4.bin" 63 | 64 | TraceError: 65 | incbin "_debugger/Part5.bin" 66 | 67 | SpuriousException: 68 | incbin "_debugger/Part6.bin" 69 | 70 | ZeroDivideError: 71 | incbin "_debugger/Part7.bin" 72 | 73 | CHKExceptionError: 74 | incbin "_debugger/Part8.bin" 75 | 76 | TRAPVError: 77 | incbin "_debugger/Part9.bin" 78 | 79 | IllegalInstrError: 80 | incbin "_debugger/PartA.bin" 81 | 82 | PrivilegeViolation: 83 | incbin "_debugger/PartB.bin" 84 | 85 | LineAEmulation: 86 | incbin "_debugger/PartC.bin" 87 | 88 | LineFEmulation: 89 | incbin "_debugger/PartD.bin" 90 | 91 | TrapVector: 92 | incbin "_debugger/PartE.bin" 93 | 94 | ; Edit this to something sensible. One suggestion is the SVN revision. 95 | RevisionNumber: 96 | dc.w 1 97 | incbin "_debugger/PartF.bin" 98 | 99 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BLACK=`echo -en '\e[2;30m'` 4 | RED=`echo -en '\e[2;31m'` 5 | GREEN=`echo -en '\e[2;32m'` 6 | YELLOW=`echo -en '\e[2;33m'` 7 | BLUE=`echo -en '\e[2;34m'` 8 | MAGENTA=`echo -en '\e[2;35m'` 9 | CYAN=`echo -en '\e[2;36m'` 10 | WHITE=`echo -en '\e[2;37m'` 11 | DEFAULT=`echo -en '\e[0;39m'` 12 | 13 | # Some cleanup 14 | rm -rf blob/*.bin *.7z *.bin *.h *.lst *.p 15 | 16 | # First of all, we assemble the binary 17 | if [[ "$OS" -eq "Windows_NT" ]]; then 18 | AS=./asw.exe 19 | P2BIN=./p2bin.exe 20 | EXEEXT=.exe 21 | else 22 | AS=asl 23 | P2BIN=p2bin 24 | EXEEXT= 25 | fi 26 | $AS -xx -c -E -q -L -r 2 -A -U -i src blob.asm 27 | 28 | if [[ -f blob.log ]]; then 29 | # There were errors or warnings when building the ROM. Print message and 30 | # print a filtered version of the log file. 31 | echo ' 32 | ***************************************************** 33 | * * 34 | * There were errors/warnings when building ROM. * 35 | * * 36 | *****************************************************' | cat - blob.log \ 37 | | sed -r "s/^(\*.*\*)$/${RED}\1${DEFAULT}/g; 38 | s/> > >([A-Za-z0-9_ '\"]+\.asm)\(([0-9]+)\)(.*):/${GREEN}>>>${MAGENTA}\1${CYAN}:${GREEN}\2${CYAN}\3:${DEFAULT}/g; 39 | :repeat; 40 | s/\x1B\[2;36m\s+([A-Za-z0-9_ '\"]+)\(([0-9]+)\)(.*):\x1B\[0;39m/${CYAN}:${MAGENTA}\1${CYAN}:${GREEN}\2${CYAN}\3:${DEFAULT}/g; 41 | t repeat; 42 | s/> > >/${GREEN}>>>${DEFAULT}/g; 43 | s/\berrors?\b:/${RED}Error${CYAN}:${DEFAULT}/gI; 44 | s/\bwarnings?\b:/${YELLOW}Warning${CYAN}:${DEFAULT}/gI" \ 45 | | less -R 46 | exit 1 47 | fi 48 | 49 | $P2BIN blob.p &> /dev/null 50 | 51 | # There are 12 bytes in jmp instructions at the start that we need to skip 52 | JumpSkip=12 53 | 54 | # Several symbols we must look for: 55 | HackerName="0x$(egrep -o '\bHackerName\s+:\s*[A-Fa-f0-9]+' blob.lst | egrep -o '\b[A-Fa-f0-9]+\b')" 56 | BusErrorMsg="0x$(egrep -o '\bBusErrorMsg\s+:\s*[A-Fa-f0-9]+' blob.lst | egrep -o '\b[A-Fa-f0-9]+\b')" 57 | BusError="0x$(egrep -o '\bBusError\s+:\s*[A-Fa-f0-9]+' blob.lst | egrep -o '\b[A-Fa-f0-9]+\b')" 58 | AddressError="0x$(egrep -o '\bAddressError\s+:\s*[A-Fa-f0-9]+' blob.lst | egrep -o '\b[A-Fa-f0-9]+\b')" 59 | TraceError="0x$(egrep -o '\bTraceError\s+:\s*[A-Fa-f0-9]+' blob.lst | egrep -o '\b[A-Fa-f0-9]+\b')" 60 | SpuriousException="0x$(egrep -o '\bSpuriousException\s+:\s*[A-Fa-f0-9]+' blob.lst | egrep -o '\b[A-Fa-f0-9]+\b')" 61 | ZeroDivideError="0x$(egrep -o '\bZeroDivideError\s+:\s*[A-Fa-f0-9]+' blob.lst | egrep -o '\b[A-Fa-f0-9]+\b')" 62 | CHKExceptionError="0x$(egrep -o '\bCHKExceptionError\s+:\s*[A-Fa-f0-9]+' blob.lst | egrep -o '\b[A-Fa-f0-9]+\b')" 63 | TRAPVError="0x$(egrep -o '\bTRAPVError\s+:\s*[A-Fa-f0-9]+' blob.lst | egrep -o '\b[A-Fa-f0-9]+\b')" 64 | IllegalInstrError="0x$(egrep -o '\bIllegalInstrError\s+:\s*[A-Fa-f0-9]+' blob.lst | egrep -o '\b[A-Fa-f0-9]+\b')" 65 | PrivilegeViolation="0x$(egrep -o '\bPrivilegeViolation\s+:\s*[A-Fa-f0-9]+' blob.lst | egrep -o '\b[A-Fa-f0-9]+\b')" 66 | LineAEmulation="0x$(egrep -o '\bLineAEmulation\s+:\s*[A-Fa-f0-9]+' blob.lst | egrep -o '\b[A-Fa-f0-9]+\b')" 67 | LineFEmulation="0x$(egrep -o '\bLineFEmulation\s+:\s*[A-Fa-f0-9]+' blob.lst | egrep -o '\b[A-Fa-f0-9]+\b')" 68 | TrapVector="0x$(egrep -o '\bTrapVector\s+:\s*[A-Fa-f0-9]+' blob.lst | egrep -o '\b[A-Fa-f0-9]+\b')" 69 | 70 | # Address of first instruction after "move.w #Revision,d0" 71 | post_revision="0x$(egrep -o '\b[A-Fa-f0-9]+\s+:\s*[A-Fa-f0-9]+\s*[A-Fa-f0-9]+\s+move\.w\s+#Revision,d0' blob.lst | egrep -o '^[0-9A-Fa-f]+')" 72 | 73 | # Last address in listing: 74 | padding_start="0x$(egrep -o '/\s+[A-Fa-f0-9]+\s*:' blob.lst | tail -n 1 | egrep -o '[A-Fa-f0-9]+')" 75 | 76 | # Now that we have every address we need, it is time to start slicing the built 77 | # binary into several chunks. 78 | dd ibs=1 skip=$((JumpSkip )) count=$((HackerName-JumpSkip )) if=blob.bin of=blob/Part1.bin status=none 79 | dd ibs=1 skip=$((BusErrorMsg )) count=$((BusError-BusErrorMsg )) if=blob.bin of=blob/Part2.bin status=none 80 | dd ibs=1 skip=$((BusError )) count=$((AddressError-BusError )) if=blob.bin of=blob/Part3.bin status=none 81 | dd ibs=1 skip=$((AddressError )) count=$((TraceError-AddressError )) if=blob.bin of=blob/Part4.bin status=none 82 | dd ibs=1 skip=$((TraceError )) count=$((SpuriousException-TraceError )) if=blob.bin of=blob/Part5.bin status=none 83 | dd ibs=1 skip=$((SpuriousException )) count=$((ZeroDivideError-SpuriousException )) if=blob.bin of=blob/Part6.bin status=none 84 | dd ibs=1 skip=$((ZeroDivideError )) count=$((CHKExceptionError-ZeroDivideError )) if=blob.bin of=blob/Part7.bin status=none 85 | dd ibs=1 skip=$((CHKExceptionError )) count=$((TRAPVError-CHKExceptionError )) if=blob.bin of=blob/Part8.bin status=none 86 | dd ibs=1 skip=$((TRAPVError )) count=$((IllegalInstrError-TRAPVError )) if=blob.bin of=blob/Part9.bin status=none 87 | dd ibs=1 skip=$((IllegalInstrError )) count=$((PrivilegeViolation-IllegalInstrError)) if=blob.bin of=blob/PartA.bin status=none 88 | dd ibs=1 skip=$((PrivilegeViolation)) count=$((LineAEmulation-PrivilegeViolation )) if=blob.bin of=blob/PartB.bin status=none 89 | dd ibs=1 skip=$((LineAEmulation )) count=$((LineFEmulation-LineAEmulation )) if=blob.bin of=blob/PartC.bin status=none 90 | dd ibs=1 skip=$((LineFEmulation )) count=$((TrapVector-LineFEmulation )) if=blob.bin of=blob/PartD.bin status=none 91 | dd ibs=1 skip=$((TrapVector )) count=$((post_revision+2-TrapVector )) if=blob.bin of=blob/PartE.bin status=none 92 | dd ibs=1 skip=$((post_revision+4 )) count=$((padding_start-(post_revision+4) )) if=blob.bin of=blob/PartF.bin status=none 93 | 94 | find . -iname '*~' -delete 95 | zipname="`pwd`/Debugger.7z" 96 | distfiles="Debugger.asm DebuggerMap.bin Disassembler.asm skcompat.asm Terminal.asm TerminalFont.bin TerminalPal.bin" 97 | ( cd src ; echo -n "Generating debugger assembly archive... " && (7z u -t7z -m0=lzma -mx=9 -mfb=64 -md=32m -ms=on $zipname $distfiles 1> /dev/null && echo "done.") || echo "failed" ) 98 | distfiles="DebuggerBlob.asm `ls blob/*.bin | sed 's%blob/%%g'`" 99 | zipname="`pwd`/DebuggerBlob.7z" 100 | ( cd blob ; echo -n "Generating debugger assembly archive... " && (7z u -t7z -m0=lzma -mx=9 -mfb=64 -md=32m -ms=on $zipname $distfiles 1> /dev/null && echo "done.") || echo "failed" ) 101 | 102 | -------------------------------------------------------------------------------- /img/address-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flamewing/genesis-debugger/20fd1d8e21379b0a733a6b6ecc9a4fa8a3622d00/img/address-error.png -------------------------------------------------------------------------------- /img/disassembler-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flamewing/genesis-debugger/20fd1d8e21379b0a733a6b6ecc9a4fa8a3622d00/img/disassembler-test.png -------------------------------------------------------------------------------- /img/div-by-zero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flamewing/genesis-debugger/20fd1d8e21379b0a733a6b6ecc9a4fa8a3622d00/img/div-by-zero.png -------------------------------------------------------------------------------- /src/Debugger.asm: -------------------------------------------------------------------------------- 1 | ; =========================================================================== 2 | ; Copyright (C) 2011-2018 by flamewing 3 | ; 4 | ; Permission to use, copy, modify, and/or distribute this software for any 5 | ; purpose with or without fee is hereby granted. 6 | ; 7 | ; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | ; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | ; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | ; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 13 | ; OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | ; =========================================================================== 15 | ; Use this to enable/disable the debugger: 16 | EnableDebugger = 1 ; 1 = enabled, 0 = disabled 17 | ; =========================================================================== 18 | ; Use this to identify the revision of your hack: 19 | ifndef Revision 20 | equ Revision,1 21 | endif 22 | ; =========================================================================== 23 | ; This should be the path to "Disassembler.asm": 24 | if EnableDebugger 25 | include "Disassembler.asm" 26 | ; =========================================================================== 27 | ; Convenience macros, for increased maintainability of the code. 28 | ifndef intMacros_defined 29 | intMacros_defined = 1 30 | enableInts macro 31 | move #$2300,sr 32 | endm 33 | 34 | disableInts macro 35 | move #$2700,sr 36 | endm 37 | endif 38 | ; =========================================================================== 39 | ; Make sure this points to the correct location. 40 | ; --------------------------------------------------------------------------- 41 | ; Enigma compressed mappings 42 | MapEng_Debugger: BINCLUDE "DebuggerMap.bin" 43 | even 44 | ; =========================================================================== 45 | ; Edit these to your name and e-mail. If the e-mail is empty (""), then nothing 46 | ; will be printed on the line. You can use all characters in the ASCII character 47 | ; set (see https://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters). 48 | HackerName: vtstring WHITE,"Your name " 49 | EMailmsg: vtstring BLUE ,"your.email@server.domain " 50 | ; =========================================================================== 51 | ; NO USER SERVICEABLE PARTS BELOW THIS POINT 52 | ; =========================================================================== 53 | ; All 16 registers are saved on stack, so we need to skip 16*4 bytes to reach 54 | ; the exception data. 55 | except_off = 16*4 56 | ; Size of stack frame of group 0 exceptions except for reset. 57 | group0_sz = 14 58 | ; Offset for saved SR in group 0 stack frame. 59 | group0_sr = 8 60 | ; Size of stack frame for group 1 and 2 exceptions. 61 | group12_sz = 6 62 | ; =========================================================================== 63 | ; System strings, do not edit. 64 | BusErrorMsg: vtstring BLUE ," A bus error has occurred " 65 | AddressErrorMsg: vtstring BLUE ,"An address error has occurred" 66 | IllegalInstrMsg: vtstring BLUE ,"Illegal instruction was found" 67 | ZeroDivideMsg: vtstring BLUE ," A division by zero happened " 68 | CHKExceptionMsg: vtstring BLUE ," Unhandled chk exception " 69 | TRAPVErrMsg: vtstring BLUE ," Unhandled integer overflow " 70 | PrivilegeViolationMsg: vtstring BLUE ," Privilege violation happened" 71 | TraceMsg: vtstring BLUE ," Trace exception occurred " 72 | LineAEmulationMsg: vtstring BLUE ,"Line 1010 emulation triggered" 73 | LineFEmulationMsg: vtstring BLUE ,"Line 1111 emulation triggered" 74 | SpuriousExceptionMsg: vtstring BLUE ,"A spurious exception happened" 75 | TrapVectorMsg: vtstring BLUE ," Unhandled trap was triggered" 76 | AtAddressmsg: vtstring BLUE ," executing address " 77 | Atmsg: vtstring WHITE,"at " 78 | ReadFrommsg: vtstring BLUE ," on a read from " 79 | WriteTomsg: vtstring BLUE ," on a write to " 80 | UnrecovDatamsg: vtstring RED ,"\x7F Unavailable information for disasm \x7F" 81 | UnrecovAddrmsg: vtstring RED ,"\x7F Unrecoverable address for disasm \x7F" 82 | UnknownAddrmsg: vtstring RED ,"\x7F Unknown address; candidates are: \x7F" 83 | UnknownAddr: vtstring RED ,"$????????:" 84 | ErrorDataMsg: vtstring BLUE ,"\x7F\x7F\x7F Error data \x7F\x7F\x7F" 85 | ErrorFlags: vtstring GREEN,"Flags: " 86 | AccessAddress: vtstring GREEN,"Access address:" 87 | IRMessage: vtstring GREEN,"ir:" 88 | ; =========================================================================== 89 | ; Macro responsible for managing the 'header' common to all error handling 90 | ; routines: disables interrupts, saves all registers, loads the message given 91 | ; as parameter, calls the common initialization code and sets (if needed) the 92 | ; instruction maks for finding the instruction. 93 | InitErrorHandler macro framesz,errmsg,rewindflag,rewindfun 94 | disableInts ; Disable interrupts 95 | pea framesz(sp) ; Save SP before exception 96 | movem.l d0-a6,-(sp) ; Save all other registers; we will need them 97 | lea errmsg(pc),a1 98 | bsr.w CommonErrorInit 99 | if "rewindflag"<>"" 100 | moveq #rewindflag,d1 101 | endif 102 | if "rewindfun"<>"" 103 | lea rewindfun(pc),a4 104 | endif 105 | move.l sp,usp ; Save current stack pointer to usp 106 | endm 107 | ; =========================================================================== 108 | BusError: 109 | InitErrorHandler group0_sz,BusErrorMsg 110 | bra.w BusAddressError_Handler 111 | ; =========================================================================== 112 | AddressError: 113 | InitErrorHandler group0_sz,AddressErrorMsg 114 | bra.w BusAddressError_Handler 115 | ; =========================================================================== 116 | TraceError: 117 | ; This tecnically should rewind to the previously executed instruction for 118 | ; the tracing; but without the IR, there is no hope for that. I have no idea 119 | ; how to properly handle this case. 120 | InitErrorHandler group12_sz,TraceMsg,0 121 | bra.w Exception_Handler 122 | ; =========================================================================== 123 | SpuriousException: 124 | ; I have absolutely no idea how to handle this case. 125 | InitErrorHandler group12_sz,SpuriousExceptionMsg,0 126 | bra.w Exception_Handler 127 | ; =========================================================================== 128 | ZeroDivideError: 129 | InitErrorHandler group12_sz,ZeroDivideMsg,-1,ChkDiv 130 | bra.w Exception_Handler 131 | ; =========================================================================== 132 | CHKExceptionError: 133 | InitErrorHandler group12_sz,CHKExceptionMsg,-1,ChkChk 134 | bra.w Exception_Handler 135 | ; =========================================================================== 136 | TRAPVError: 137 | InitErrorHandler group12_sz,TRAPVErrMsg,-1,ChkTrapV 138 | bra.w Exception_Handler 139 | ; =========================================================================== 140 | IllegalInstrError: 141 | InitErrorHandler group12_sz,IllegalInstrMsg,0 142 | bra.w Exception_Handler 143 | ; =========================================================================== 144 | PrivilegeViolation: 145 | InitErrorHandler group12_sz,PrivilegeViolationMsg,0 146 | bra.w Exception_Handler 147 | ; =========================================================================== 148 | LineAEmulation: 149 | InitErrorHandler group12_sz,LineAEmulationMsg,0 150 | bra.w Exception_Handler 151 | ; =========================================================================== 152 | LineFEmulation: 153 | InitErrorHandler group12_sz,LineFEmulationMsg,0 154 | bra.w Exception_Handler 155 | ; =========================================================================== 156 | TrapVector: 157 | InitErrorHandler group12_sz,TrapVectorMsg,-1,ChkTrap 158 | bra.w Exception_Handler 159 | ; =========================================================================== 160 | ; Do initial setup of screen so that the system is in a known state. 161 | ; Input: 162 | ; a1 Error message to print 163 | CommonErrorInit: 164 | move.l a1,-(sp) 165 | bsr.w InitTerminal 166 | 167 | ; Decode enigma-compressed plane mappings to RAM buffer. 168 | lea MapEng_Debugger(pc),a0 169 | lea (Chunk_Table).l,a1 170 | move.w #make_art_tile(ArtTile_VRAM_Start,0,0),d0 171 | ifdef debugger_blob 172 | jsr EniDec(pc) 173 | else 174 | jsr (EniDec).w 175 | endif 176 | 177 | ; Print the type of error. 178 | movea.l (sp)+,a1 179 | setcursor 5, 0 180 | bsr.w Print_Message 181 | 182 | ; Print out hacker name. 183 | lea HackerName(pc),a1 184 | setcursor 28, 3 185 | bsr.w Print_Message 186 | 187 | ; Print e-mail and "at" text, if required. 188 | move.w EMailmsg(pc),d0 ; Is e-mail string empty? 189 | beq.s .skip_email ; Branch if yes 190 | ; Print the "at" message, then the e-mail. 191 | setcursor 1, 4 192 | lea Atmsg(pc),a1 193 | bsr.w Print_Message 194 | lea EMailmsg(pc),a1 195 | bsr.w Print_Message 196 | 197 | .skip_email: 198 | ; Print hack revision. 199 | setcursor 9, 15 200 | move.w #Revision,d0 201 | bsr.w Print_Word 202 | 203 | ; Print the stack pointer -- but adjust it to skip all registers we added. 204 | setcursor 30, 15 205 | move.l sp,d0 206 | addi.l #15*4+4,d0 ; 15 registers + return address 207 | bsr.w Print_Long 208 | 209 | ; Print the user stack pointer. 210 | setcursor 30, 14 211 | move.l usp,a0 ; Get usp 212 | move.l a0,d0 213 | bsr.w Print_Long 214 | 215 | ; Print the registers as they were at the start of the exception handler. 216 | lea 4(sp),a0 ; Skip return address 217 | setcursor 4, 17,a2 218 | moveq #4,d3 ; 5 lines of registers 219 | 220 | ; Dump all registers. 221 | .reg_row_loop: 222 | moveq #2,d4 ; 3 columns of registers per line 223 | seekset a2 ; Simplifies line wrapping 224 | 225 | .reg_col_loop: 226 | move.l (a0)+,d0 227 | bsr.w Print_Long 228 | seekp.w 4 ; Advance to next column 229 | dbra d4,.reg_col_loop 230 | seekp.w nCols,a2 ; Advance to next line 231 | dbra d3,.reg_row_loop 232 | 233 | rts 234 | ; =========================================================================== 235 | BusAddressError_Handler: 236 | ; a0 = pointer to exception data. 237 | move.l usp,a0 238 | adda.w #except_off,a0 ; 16 registers 239 | 240 | ; First, the interrupt flags. 241 | move.w (a0)+,d0 ; Get interrupt flags 242 | ; Print the "Flags: " text 243 | lea ErrorFlags(pc),a1 244 | setcursor 1, 13 245 | bsr.w Print_Message 246 | tellp.l a2 ; Destination for the flags 247 | ; Print information on whether or not the exception happened during a read 248 | ; or during a write. An exception can happen on a read if executing non- 249 | ; aligned code. 250 | move.w #'W'|RED, d1 251 | lea WriteTomsg(pc),a1 252 | btst #4,d0 ; Did the error happen during a read? 253 | beq.s .print_rw ; Branch if not 254 | ; On a read, must also display the 'read from' bit. 255 | move.w #'R'|RED, d1 256 | lea ReadFrommsg(pc),a1 257 | 258 | .print_rw: 259 | setcursor 5, 1 260 | bsr.w Print_Message 261 | seekset.l a2 262 | vtput.w d1 263 | 264 | ; Whether or not the exception was on a valid instruction. 265 | move.w #'I'|RED, d1 266 | btst #3,d0 ; Is it a valid instruction? 267 | beq.s .print_in ; Branch if yes 268 | move.w #'N'|RED, d1 269 | 270 | .print_in: 271 | vtput.w d1 272 | 273 | moveq #2,d2 274 | 275 | ; Function codes: FC? low/high. See user manual. 276 | .fc_code_loop: 277 | move.w #'L'|RED, d1 278 | btst d2,d0 ; Is FC low? 279 | beq.s .print_hl ; Branch if yes 280 | move.w #'H'|RED, d1 281 | 282 | .print_hl: 283 | vtput.w d1 284 | dbra d2,.fc_code_loop 285 | 286 | setcursor 11, 12 287 | lea ErrorDataMsg(pc),a1 288 | bsr.w Print_Message 289 | 290 | ; Print the misaligned address that caused the exception. 291 | move.l (a0)+,d0 ; Read access address 292 | setcursor 23, 1 293 | bsr.w Print_Address 294 | vtputc "!" 295 | ; Print the "Access address:" text 296 | lea AccessAddress(pc),a1 297 | setcursor 17, 13 298 | bsr.w Print_Message 299 | bsr.w Print_Address 300 | 301 | ; Print the instruction register (IR) 302 | move.w (a0)+,d0 ; Read IR to d0 303 | move.w d0,d5 ; Save IR for later 304 | ; Print the "IR:" text 305 | lea IRMessage(pc),a1 306 | setcursor 15, 15 307 | bsr.w Print_Message 308 | ; Instruction Register. 309 | bsr.w Print_Word 310 | 311 | ; Print the status register (SR). 312 | move.w (a0)+,d0 ; Read SR to d0 313 | ; Status Register. 314 | setcursor 4, 14 315 | bsr.w Print_Word 316 | 317 | ; Print location of the next instruction to be executed -- Program Counter (PC). 318 | move.l (a0)+,d0 ; Read PC 319 | movea.l d0,a3 ; Save it for later 320 | setcursor 18, 14 321 | bsr.w Print_Address 322 | 323 | ; Instruction disassembly. Needs the raw instruction in d5 and PC as given 324 | ; by the exception data: either for the next opcode to execute or, for move 325 | ; instructions, it may be right before the destination extension word if the 326 | ; error happened when reading the source. 327 | setcursor DisassemblyAlign, 9 328 | lea RewindBusAddressError(pc),a4 329 | pea .chk_disasm(pc) ; Makes SetFullRecovery return to this. 330 | pea SetFullRecovery(pc) ; Makes Disassemble_Opcode return to this 331 | bra.w Disassemble_Opcode ; Will eventually return to the following line 332 | 333 | .chk_disasm: 334 | bne.s DisassemblyFailed ; Return is nonzero if information was lost 335 | ; Disassembly was complete. 336 | ; Print the address of the instruction. 337 | move.l a3,d0 ; Copy PC; this points to the word after what caused the error 338 | subq.l #2,d0 ; All instruction opcodes are 2 bytes 339 | setcursor 1, 8 340 | bsr.w Print_Address 341 | vtputc ":" 342 | bra.s PrepareStackTrace 343 | ; --------------------------------------------------------------------------- 344 | DisassemblyFailed: 345 | ; Missing information led to incomplete disassembly. 346 | ; This happens for Bcc (except BSR), JMP (but not JSR) and DBcc when cc is 347 | ; false or when cc true, but the register was not -1 (i.e., loop not done). 348 | ; The Bcc and DBcc cases can't trigger address errors, and the Genesis does 349 | ; not ever generate bus errors; so this will usually only happen for JMP. 350 | setcursor 1, 11 351 | lea UnrecovDatamsg(pc),a1 352 | bmi.s .chk_v_flag ; lea does not affect condition codes 353 | ; We could recover all but instruction address. 354 | lea UnrecovAddrmsg(pc),a1 355 | 356 | .chk_v_flag: 357 | bvc.s .print_miss_msg ; lea does not affect condition codes 358 | setcursor 1, 10 359 | lea UnknownAddrmsg(pc),a1 360 | 361 | .print_miss_msg: 362 | bsr.w Print_Message 363 | lea UnknownAddr(pc),a1 364 | setcursor 1, 8 365 | bsr.w Print_Message 366 | 367 | PrepareStackTrace: 368 | ; Print stack dump -- starting AFTER all exception data. 369 | moveq #group0_sz,d1 ; Size of address/bus error stack frame 370 | bra.w FinishErrorDump 371 | ; =========================================================================== 372 | Exception_Handler: 373 | ; a0 = pointer to exception data. 374 | move.l usp,a0 375 | adda.w #except_off,a0 ; 16 registers 376 | 377 | lea AtAddressmsg(pc),a1 378 | setcursor 5, 1 379 | bsr.w Print_Message 380 | 381 | setcursor 11, 13 382 | lea ErrorDataMsg(pc),a1 383 | bsr.w Print_Message 384 | 385 | ; Print the status register (SR). 386 | move.w (a0)+,d0 ; Read SR to d0 387 | ; Status Register. 388 | setcursor 4, 14 389 | bsr.w Print_Word 390 | 391 | ; Get location of the instruction -- Program Counter (PC). 392 | movea.l (a0)+,a3 ; Read PC 393 | 394 | tst.w d1 ; Do we need to scan for the instruction? 395 | beq.s .found_instr ; Branch if not 396 | moveq #0,d2 ; Number of bytes found in extension words 397 | 398 | .prev_word: 399 | move.w -(a3),d5 ; Get previous word 400 | jsr (a4) ; Is this the instruction? 401 | beq.s .found_instr ; Branch if yes 402 | addq.w #2,d2 ; Two more bytes 403 | bra.s .prev_word ; Loop 404 | 405 | .found_instr: 406 | move.l a3,d0 ; Print PC 407 | setcursor 17, 14 408 | bsr.w Print_Address 409 | setcursor 24, 1 410 | bsr.w Print_Address 411 | vtputc "!" 412 | setcursor 1, 8 413 | bsr.w Print_Address 414 | vtputc ":" 415 | 416 | ; Instruction disassembly. Needs the raw instruction in d5 and PC after the 417 | ; opcode in a3. 418 | move.w (a3)+,d5 ; Read instruction 419 | setcursor DisassemblyAlign, 9 420 | lea RewindStub(pc),a4 421 | bsr.w Disassemble_Opcode 422 | moveq #group12_sz,d1 ; Size of group 1 & 2 exception frame 423 | ; Fall through. 424 | ;bra.w FinishErrorDump 425 | ; =========================================================================== 426 | ; SUBROUTINE 427 | ; Prints stack dump, maps the buffer onto VRAM and locksthe machine. 428 | ; Input: 429 | ; d1 How many bytes to skip after the registers on the stack. 430 | FinishErrorDump: 431 | move.l usp,a0 432 | lea except_off(a0,d1.w),a0 ; Pointer to data to dump. 433 | setcursor 0, 23,a2 434 | move.w a0,d3 435 | subi.w #System_Stack,d3 ; d3 = -1 * number of bytes to print 436 | bpl.s NoStackTrace ; Branch if positive or zero (stack underflow or empty) 437 | neg.w d3 438 | cmpi.w #$50,d3 ; Limit to $50 bytes, as this is all space we have. 439 | blo.s .have_byte_count 440 | moveq #$50,d3 441 | 442 | .have_byte_count: 443 | lsr.w #1,d3 ; Convert to words. 444 | move.w d3,d5 445 | addq.w #7,d3 ; Prepare to round up number of lines. 446 | lsr.w #3,d3 ; Convert to lines of stack dump. 447 | subq.w #1,d3 ; Turn into loop index. 448 | andi.w #7,d5 ; Number of words we have to print on last line. 449 | bne.s .have_last_row_cnt ; Branch if nonzero 450 | moveq #8,d5 ; Otherwise, we need to print an entire line 451 | 452 | .have_last_row_cnt: 453 | swap d1 ; Save exception size 454 | lsr.w #1,d5 ; Convert to number of longs... 455 | scs.b d1 ; ... but set d1 to true if we have a last word. 456 | swap d1 ; Save "last word" flag and restore exception size 457 | subq.w #1,d5 ; Convert this to a loop index too 458 | 459 | ; Stack dump loop. 460 | .stack_row_loop 461 | moveq #3,d4 ; 4 columns of stack dump 462 | tst.w d3 ; Do we have more than one line left? 463 | bne.s .row_not_empty ; Branch if yes 464 | move.w d5,d4 ; Number of longwords for last line 465 | bmi.s StackDump_Done ; If this line is empty, we are done 466 | 467 | .row_not_empty: 468 | seekset a2 ; Simplifies line wrapping 469 | vtputc "+" 470 | exg.l d0,d1 471 | bsr.w HexDump_Byte 472 | exg.l d0,d1 473 | addi.w #$10,d1 474 | vtputc " " 475 | 476 | .stack_col_loop: 477 | move.l (a0)+,d0 478 | bsr.w HexDump_Long 479 | seekp.w 1 ; Advance to next column 480 | dbra d4,.stack_col_loop 481 | seekp.w nCols,a2 ; Advance to next line 482 | dbra d3,.stack_row_loop 483 | 484 | StackDump_Done: 485 | ; If we have a last word (as opposed to last long), we need to delete the 486 | ; last two characters printed. 487 | swap d1 ; Get "last word" flag. 488 | tst.b d1 489 | beq.s .no_clr_word 490 | seekp.w -5 ; Roll back 4 hex digits and one space 491 | vtputs " " ; Clear these digits. 492 | .no_clr_word: 493 | 494 | NoStackTrace: 495 | bsr.w DrawTerminal 496 | 497 | startZ80 498 | 499 | movem.l (sp)+,d0-a6 ; Restore all registers 500 | addq.w #4,sp ; Pop off the saved SP as well 501 | 502 | ; SP is now back to the same point it was at the start of the exception. 503 | 504 | enableInts ; Enable interrupts 505 | 506 | ; Exception loop. 507 | .error_trap: 508 | nop 509 | nop 510 | bra.s .error_trap 511 | ; =========================================================================== 512 | RewindStub: 513 | rts 514 | ; =========================================================================== 515 | ; Adjusts a3 to be just after the instruction in d5 if possible. This is not 516 | ; possible for some dbCC cases, for jmp and for bCC other than bsr; in these 517 | ; cases, finishes the disassembly and the sets condition codes as appropriate 518 | ; and DOES NOT RETURN TO CALLER in this case. 519 | ; Input: 520 | ; usp Pointer to saved registers (d0-a6,sr) 521 | ; a3 For normal, forward disassembly, a3 is PC after the opcode, but before 522 | ; any the extension words, if any; in this case, the function at (a4) must 523 | ; be a no-nop. 524 | ; For rewinding disassembly (address and bus errors), a3 is PC for either 525 | ; the next instruction to execute or, for move instructions only, it may 526 | ; be right before the destination extension word if the error happened 527 | ; when reading from the source. In this case, the function at (a4) should 528 | ; handle rewinding to just after the opcode for all instructions, that is, 529 | ; to the same location that would be in a3 in a forward disassembly. 530 | ; a5 Where the output text goes 531 | ; d1 Effective address bits 532 | ; d2 For bCC and dbCC instructions, 4 * the condition code from IR. It is not 533 | ; used for other instructions. 534 | ; d3 Instruction size (%00 = byte, %01 = word, %10 = long, all others = invalid) 535 | ; d5 Instruction register 536 | ; Output: 537 | ; a3 Program counter of instruction disassembled + 2 (before extension words) 538 | ; a5 Cell just after last printed text 539 | ; If the disassembly was successful and recovered all information, then zero 540 | ; flag is set; if information was lost, zero flag is cleared. 541 | ; If the only information lost was the PC for the opcode, then negative flag 542 | ; if cleared; if more information than that was lost than that, this flag is 543 | ; set instead. 544 | ; The overflow flag is set if and only if the zero flag is clear AND if it was 545 | ; possible to infer the address of the instruction, but there were multiple 546 | ; possible sources. 547 | RewindBusAddressError: 548 | move.w d5,d0 549 | andi.w #$FFC0,d0 550 | cmpi.w #$4E80,d0 ; Is it a jsr? 551 | beq.s .got_jsr ; Branch if yes 552 | cmpi.w #$4EC0,d0 ; Is it a jmp? 553 | beq.w .got_jmp ; Branch if yes 554 | move.w d5,d0 555 | andi.w #$F0F8,d0 556 | cmpi.w #$50C8,d0 ; Was this a dbCC? 557 | beq.s .got_dbCC ; Branch if yes 558 | andi.w #$F000,d0 559 | cmpi.w #$6000,d0 ; Was this a bCC? 560 | beq.s .got_bCC ; Branch if yes 561 | ; Filter out move instructions, as they need a bit more care. 562 | rol.w #4,d0 563 | beq.s .not_move ; Not a move 564 | subq.b #4,d0 565 | bmi.s .got_move ; Branch if a move 566 | 567 | .not_move: 568 | suba.w d4,a3 569 | 570 | .end_normal: 571 | bsr.w Calc_effective_address_size 572 | suba.w d4,a3 573 | rts 574 | ; --------------------------------------------------------------------------- 575 | .got_jsr: 576 | move.l usp,a0 577 | movea.l except_off+group0_sz(a0),a3 ; a3 = pointer to jsr return target. 578 | moveq #2,d3 ; Pretend long immediate, even though invalid 579 | bsr.w Calc_effective_address_size 580 | suba.w d4,a3 ; Adjust address for first parameter 581 | rts 582 | ; --------------------------------------------------------------------------- 583 | .got_move: 584 | exg.l d5,d1 585 | bsr.w Calc_effective_address_size 586 | suba.w d4,a3 ; Adjust address for first parameter 587 | exg.l d5,d1 588 | move.l usp,a0 589 | move.w except_off(a0),d0 ; Address error flags 590 | btst #4,d0 ; Did the error happen during a read? 591 | beq.s .end_normal ; Branch if not 592 | rts 593 | ; --------------------------------------------------------------------------- 594 | .got_dbCC: 595 | move.l usp,a0 596 | move.w except_off+group0_sr(a0),sr ; Fetch value of sr at the time of the exception. 597 | ; If the condition was true, we can recover everything; however, this case 598 | ; never happens for address errors. It is handled here for completness, as 599 | ; other systems than the Genesis may generate bus errors and it will, thus, 600 | ; become relevant. 601 | bsr.w Check_Condition ; Was the condition true? 602 | bne.s .dbCC_complete ; Branch if yes (can recover everything). 603 | ; Get the counter value; if it is -1, then we still can recover everything, 604 | ; with the same caveats for address errors as above. 605 | move.w d5,d0 606 | andi.w #7,d0 607 | lsl.w #4,d0 608 | move.l (a0,d0.w),d0 609 | ; So was the loop counter -1? 610 | cmpi.w #-1,d0 611 | beq.s .dbCC_complete ; Branch if yes. 612 | bra.s .bCC_dbCC_target_only 613 | ; --------------------------------------------------------------------------- 614 | .got_bCC: 615 | move.b d5,d3 ; Get displacement 616 | beq.s .not_short_branch ; Branch if not a short branch 617 | ; We can always recover everything for a short branch. 618 | ext.w d3 619 | neg.w d3 620 | lea (a3,d3.w),a3 621 | rts 622 | ; --------------------------------------------------------------------------- 623 | .not_short_branch: 624 | tst.w d2 625 | beq.s .bCC_dbCC_target_only ; This is a bra, unfortunatelly. 626 | cmpi.b #4,d2 ; Is this a bsr? 627 | beq.s .got_bsr ; Branch if yes. 628 | ; This is a generic bCC. 629 | move.l usp,a0 630 | move.w except_off+group0_sr(a0),sr ; Fetch value of sr at the time of the exception. 631 | ; If the condition was false, we can recover everything; however, this case 632 | ; never happens for address errors. It is handled here for completness, as 633 | ; other systems than the Genesis may generate bus errors and it will, thus, 634 | ; become relevant. 635 | bsr.w Check_Condition ; Was the condition true? 636 | beq.s .bCC_complete ; Branch if not (can recover everything). 637 | 638 | .bCC_dbCC_target_only: 639 | ; Last ditch effort: try searching for the branch. 640 | bsr.w WordBranch_ScanSource 641 | beq.s .not_found ; Branch if not found (this should be impossible) 642 | bvc.s .done ; Branch if only one match was found 643 | 644 | addq.w #8,sp ; Don't return to caller, nor to its caller 645 | pea SetInferredRecovery(pc) ; Replace "new" return with this 646 | bra.s .print_branch_addr 647 | ; --------------------------------------------------------------------------- 648 | .not_found: 649 | ; Flag as intruction's address being lost 650 | addq.w #8,sp ; Don't return to caller, nor to its caller 651 | pea SetLostAddress(pc) ; Replace "new" return with this 652 | 653 | .print_branch_addr: 654 | ; a3 points to the target of the branch, so print it 655 | move.l a3,d0 656 | lea (a3),a2 657 | bra.w Print_Address 658 | ; --------------------------------------------------------------------------- 659 | .got_bsr: 660 | move.l usp,a0 661 | movea.l except_off+group0_sz(a0),a3 ; a3 = pointer to bsr return target. 662 | 663 | .bCC_complete: 664 | tst.b d5 665 | bne.s .done 666 | 667 | .dbCC_complete: 668 | subq.l #2,a3 669 | 670 | .done: 671 | rts 672 | ; --------------------------------------------------------------------------- 673 | .got_jmp: 674 | addq.w #8,sp ; Don't return to caller 675 | pea SetLostAddress(pc) ; Replace "new" return with this 676 | lea (a3),a2 677 | move.w d5,d0 678 | move.w d5,d1 679 | andi.w #$38,d0 680 | lsr.w #2,d0 681 | andi.w #$7,d1 682 | move.w jmp_EA_CodeMap(pc,d0.w),d0 683 | jmp jmp_EA_CodeMap(pc,d0.w) 684 | ; =========================================================================== 685 | jmp_EA_CodeMap: offsetTable 686 | offsetTableEntry.w InvalidAddrMode ; %000 687 | offsetTableEntry.w InvalidAddrMode ; %001 688 | offsetTableEntry.w Reg_Addr_Indirect ; %010 689 | offsetTableEntry.w InvalidAddrMode ; %011 690 | offsetTableEntry.w InvalidAddrMode ; %100 691 | offsetTableEntry.w jmp_Reg_Addr_Disp_Indirect ; %101 692 | offsetTableEntry.w jmp_Reg_Addr_Index_Indirect ; %110 693 | offsetTableEntry.w jmp_Misc_EA_Modes ; %111 694 | ; =========================================================================== 695 | jmp_Reg_Addr_Disp_Indirect: 696 | ;d16(An) 697 | ; We can recover everything but instruction address. 698 | move.w d1,d0 ; d0 = register # (0-7) of An 699 | lsl.w #2,d0 ; d0 = number of bytes before register in its register group 700 | move.l usp,a0 701 | move.l $20(a0,d0.w),d0 ; $20 = number of bytes before first address register; d0 = An 702 | sub.l a3,d0 ; d0 = -d16 703 | neg.l d0 ; d0 = d16 704 | beq.w Reg_Addr_Indirect 705 | bsr.w Print_Word_Signed 706 | bra.w Reg_Addr_Indirect 707 | ; =========================================================================== 708 | jmp_Reg_Addr_Index_Indirect: 709 | ;d8(An,Xn.S) 710 | ; We can recover nothing. 711 | addq.w #4,sp 712 | pea SetLostData(pc) ; Replace "new" return with this 713 | vtputs "$??(",RED 714 | bsr.w Reg_Addr_Direct 715 | vtputs ",Xn) \x3B ",RED ; \x3B = ';' -- this works around a bug in AS parser 716 | move.l a3,d0 717 | bra.w Print_Address 718 | ; =========================================================================== 719 | jmp_Misc_EA_Modes: 720 | lsl.w #1,d1 721 | move.w jmp_Misc_EACodeMap(pc,d1.w),d1 722 | jmp jmp_Misc_EACodeMap(pc,d1.w) 723 | ; =========================================================================== 724 | jmp_Misc_EACodeMap: offsetTable 725 | offsetTableEntry.w jmp_Abs_Short ; %000 726 | offsetTableEntry.w jmp_Abs_Long ; %001 727 | offsetTableEntry.w jmp_PC_Disp_Indirect ; %010 728 | offsetTableEntry.w jmp_PC_Index_Indirect ; %011 729 | offsetTableEntry.w InvalidAddrMode ; %100 730 | offsetTableEntry.w InvalidAddrMode ; %101 731 | offsetTableEntry.w InvalidAddrMode ; %110 732 | offsetTableEntry.w InvalidAddrMode ; %111 733 | ; =========================================================================== 734 | jmp_Abs_Short: 735 | ; We can recover everything but instruction address. 736 | vtputc "(" 737 | move.l a3,d0 738 | ext.l d0 739 | bsr.w Print_Address 740 | vtputs ").w",BLUE 741 | rts 742 | ; =========================================================================== 743 | jmp_Abs_Long: 744 | ; We can recover everything but instruction address. 745 | vtputc "(" 746 | move.l a3,d0 747 | bsr.w Print_Address 748 | vtputs ").l",BLUE 749 | rts 750 | ; =========================================================================== 751 | jmp_PC_Disp_Indirect: 752 | ;d16(PC) 753 | ; We may be able to recover everything. 754 | bsr.w WordBranch_ScanSource ; Try to find the source branch 755 | beq.s .not_found ; Branch if not found (this should be impossible) 756 | bvs.s .found_many ; Branch if found more than one 757 | ; Victory snatched from the jaws of defeat! We have recovered 758 | ; everything here. 759 | addq.w #4,sp 760 | lea (a2),a3 ; Store found location to a2 761 | pea SetFullRecovery(pc) ; Replace "new" return with this 762 | bra.w PC_Disp_Indirect 763 | ; --------------------------------------------------------------------------- 764 | .found_many: 765 | addq.w #4,sp 766 | pea SetInferredRecovery(pc) ; Replace "new" return with this 767 | 768 | .not_found: 769 | move.l a3,d0 770 | bsr.w Print_Address 771 | vtputs "(pc)",GREEN 772 | rts 773 | ; =========================================================================== 774 | jmp_PC_Index_Indirect: 775 | ;d8(PC,Xn.S) 776 | ; We may be able to recover everything. 777 | addq.w #4,sp 778 | pea SetLostData(pc) ; Replace "new" return with this 779 | ;move.l a3,d0 780 | ;bsr.w Print_Address 781 | vtputs "$??",RED 782 | vtputs "(pc,",GREEN 783 | vtputs ",Xn) \x3B ",RED ; \x3B = ';' -- this works around a bug in AS parser 784 | move.l a3,d0 785 | bra.w Print_Address 786 | ; =========================================================================== 787 | ; SUBROUTINE 788 | ; Checks if the condition of a bCC or a dbCC was true of false. 789 | ; Input: 790 | ; d2 Condition code * 4 to test. 791 | ; Output: 792 | ; Zero flag set if condition was false, unset if it was true. 793 | Check_Condition: 794 | lea Check_CC(pc,d2.w),a1 795 | jsr (a1) 796 | tst.b d0 797 | rts 798 | ; --------------------------------------------------------------------------- 799 | ChkCC macro cond 800 | cond d0 801 | rts 802 | endm 803 | ; Handlers for condition checks. 804 | Check_CC: 805 | ChkCC st.b 806 | ChkCC sf.b 807 | ChkCC shi.b 808 | ChkCC sls.b 809 | ChkCC scc.b 810 | ChkCC scs.b 811 | ChkCC sne.b 812 | ChkCC seq.b 813 | ChkCC svc.b 814 | ChkCC svs.b 815 | ChkCC spl.b 816 | ChkCC smi.b 817 | ChkCC sge.b 818 | ChkCC slt.b 819 | ChkCC sgt.b 820 | ChkCC sle.b 821 | ; =========================================================================== 822 | ; SUBROUTINE 823 | ; Validates an opcode as a divs or divl instruction. 824 | ; Input: 825 | ; d2 Number of bytes of extension words found so far. 826 | ; d5 Instruction to validate as divs/divu. 827 | ; Zero flag set if this is a valid divs or divl, clear otherwise. 828 | ChkDiv: 829 | move.w d5,d0 830 | andi.w #$F0C0,d0 ; Get the mask bits 831 | cmpi.w #$80C0,d0 ; Is this equal to the bits for the instruction? 832 | bne.s .done ; Branch if not 833 | moveq #1,d3 ; Word-sized 834 | move.w d5,d1 835 | bsr.w Calc_effective_address_size 836 | cmp.w d2,d4 ; Check if the ea size matches what was found. 837 | 838 | .done: 839 | rts 840 | ; =========================================================================== 841 | ; SUBROUTINE 842 | ; Validates an opcode as a chk instruction. 843 | ; Input: 844 | ; d2 Number of bytes of extension words found so far. 845 | ; d5 Instruction to validate as chk. 846 | ; Zero flag set if this is a valid chk, clear otherwise. 847 | ChkChk: 848 | move.w d5,d0 849 | andi.w #$F040,d0 ; Get the mask bits 850 | cmpi.w #$4000,d0 ; Is this equal to the bits for the instruction? 851 | bne.s .done ; Branch if not 852 | moveq #1,d3 ; Word-sized 853 | move.w d5,d1 854 | bsr.w Calc_effective_address_size 855 | cmp.w d2,d4 ; Check if the ea size matches what was found. 856 | 857 | .done: 858 | rts 859 | ; =========================================================================== 860 | ; SUBROUTINE 861 | ; Validates an opcode as a trapv instruction. 862 | ; Input: 863 | ; d2 Number of bytes of extension words found so far. 864 | ; d5 Instruction to validate as trapv. 865 | ; Zero flag set if this is a valid trapv, clear otherwise. 866 | ChkTrapV: 867 | move.w d5,d0 868 | andi.w #$4E76,d0 ; Get the mask bits 869 | cmpi.w #$4E76,d0 ; Is this equal to the bits for the instruction? 870 | rts 871 | ; =========================================================================== 872 | ; SUBROUTINE 873 | ; Validates an opcode as a trap instruction. 874 | ; Input: 875 | ; d2 Number of bytes of extension words found so far. 876 | ; d5 Instruction to validate as trap. 877 | ; Zero flag set if this is a valid trap, clear otherwise. 878 | ChkTrap: 879 | move.w d5,d0 880 | andi.w #$FFF0,d0 ; Get the mask bits 881 | cmpi.w #$4E40,d0 ; Is this equal to the bits for the instruction? 882 | rts 883 | ; =========================================================================== 884 | ; Computes size of affective address data 885 | ; Input: 886 | ; d1 Effective address bits 887 | ; d3 Instruction size (%00 = byte, %01 = word, %10 = long, all others = invalid) 888 | ; Output: 889 | ; d4 Size (in bytes) of data for effective address 890 | Calc_effective_address_size: 891 | moveq #0,d4 ; Start with zero bytes 892 | move.w d1,d0 893 | andi.w #$38,d0 894 | cmpi.b #$20,d0 ; Reg (data|addr) direct, reg addr indirect, reg addr post-incr indirect, reg addr pre-decr indirect? 895 | ble.s .done ; rts if so 896 | addq.w #2,d4 ; All others have at least one word 897 | cmpi.b #$38,d0 ; Misc EA modes? 898 | bne.s .done ; rts if not 899 | move.w d1,d0 900 | andi.w #$7,d0 901 | cmpi.b #$1,d0 ; Abs long 902 | beq.s .is_long ; Need 2 more bytes 903 | cmpi.b #$4,d0 ; Abs short, PC disp indirect, PC index indirect? 904 | blt.s .done ; rts if so 905 | cmpi.b #2,d3 ; Long immed? 906 | blo.s .done ; rts if not 907 | 908 | .is_long: 909 | addq.w #2,d4 ; Two words 910 | 911 | .done: 912 | rts 913 | ; =========================================================================== 914 | SetFullRecovery: 915 | move.b #%00100,ccr ; Zero flag 916 | rts 917 | ; =========================================================================== 918 | SetInferredRecovery: 919 | move.b #%00010,ccr ; Carry flag 920 | rts 921 | ; =========================================================================== 922 | SetLostAddress: 923 | move.b #%00000,ccr 924 | rts 925 | ; =========================================================================== 926 | SetLostData: 927 | move.b #%01000,ccr ; Negative flag 928 | rts 929 | ; =========================================================================== 930 | ; Tries to search for a word-sized branch to a given destination. If multiple 931 | ; such branches exist, up to 4 of them are printed to DisplayCell(1, 11). 932 | ; Input: 933 | ; a3 Target of branch 934 | ; a5 Write destination (in RAM) 935 | ; d5 Instruction register 936 | ; Output: 937 | ; a2 Program counter before the extension word of the branch instruction if 938 | ; only a single match was found, or to the first match found for multiple 939 | ; matches; unchanged otherwise. 940 | ; a3 For single matches, equal to a2; otherwise, unchanged. 941 | ; d5 High word changed to the extension word of the last match found. 942 | ; If the zero flag is set, no matches were found in the search area. 943 | ; If the overflow flag is set, several matches were found in the search area. 944 | ; If neither is set, a single match was found. 945 | WordBranch_ScanSource: 946 | move.l a3,d0 ; Copy destination address 947 | bclr #0,d0 ; Strip off lowest bit so ge have a valid address... 948 | st.b d2 ; ... but make sure we know if it was 1 949 | move.l #-$8000,d1 ; Offset back for the search 950 | add.l d1,d0 ; d0 is the first address of the search 951 | movea.l d0,a0 ; Copy it to a0 952 | cmpa.l (ROMEndLoc).w,a0 ; Is start address past the end of ROM? 953 | bls.s .start_in_rom_ram ; Branch if not 954 | cmpa.l #$FFFF0000,a0 ; Is start address in RAM? 955 | bhs.s .start_in_rom_ram ; Branch if yes 956 | movea.l #$FFFF0000,a0 ; Set to start at the start of RAM 957 | 958 | .start_in_rom_ram: 959 | cmp.l (ROMEndLoc).w,d0 ; Is it before the end of ROM? 960 | addi.l #$FFFE,d0 ; Generate the final address in the searh 961 | cmp.l (ROMEndLoc).w,d0 ; Is it before the end of ROM? 962 | bls.s .not_end_of_rom ; Branch if yes 963 | move.l (ROMEndLoc).w,d0 ; Otherwise cap it to the right value 964 | 965 | .not_end_of_rom: 966 | sub.l a0,d0 ; Prepare to make it a counter 967 | addq.l #1,d0 ; Correct number of bytes 968 | lsr.l #1,d0 ; Turn into number of words 969 | subq.l #1,d0 ; Make it into a loop counter 970 | bmi.s .done ; Abort the search if we ended up with zero words to search 971 | move.w d0,d1 ; Should be a word by now, or something is very wrong 972 | 973 | swap d5 ; Move instruction to high word 974 | move.w #$7FFE,d5 ; d5 is now instruction + extension word for start address 975 | ext.w d2 976 | sub.w d2,d5 ; And we now compensated for the low bit of the address 977 | moveq #0,d2 ; d2 will be a match counter 978 | 979 | .loop: 980 | cmp.l (a0),d5 ; Is this current location the right one? 981 | bne.s .next_word ; Branch if not 982 | tst.w d2 ; Have we found any previous matches? 983 | beq.s .found_first ; Branch if not 984 | cmpi.w #4,d2 ; Have we found too many matches? 985 | blt.s .keep_searching ; Branch if not 986 | addq.w #1,d2 ; Flag as so... 987 | bra.s .search_done ; ... and stop the search 988 | ; --------------------------------------------------------------------------- 989 | .keep_searching: 990 | pea (a0) ; Store found location in stack 991 | bra.s .chknext ; And go to the next iteration 992 | ; --------------------------------------------------------------------------- 993 | .found_first: 994 | lea 2(a0),a2 ; Store found location to a2 995 | 996 | .chknext: 997 | addq.w #1,d2 ; Flag that we found yet one more 998 | 999 | .next_word: 1000 | subq.w #2,d5 ; Change extension word 1001 | addq.l #2,a0 ; Change source address 1002 | dbra d1,.loop 1003 | 1004 | .search_done: 1005 | swap d5 ; Put instruction back in low word 1006 | tst.w d2 ; Have we found any matched 1007 | beq.s .done ; No (this should be impossible?) 1008 | cmpi.w #1,d2 ; Have we found a single match? 1009 | bne.s .found_many ; Branch if not 1010 | ; Victory snatched from the jaws of defeat! We have recovered 1011 | ; everything here. 1012 | lea (a2),a3 ; Store found location to a2 1013 | move.b #%00000,ccr ; All zero = "found the one" 1014 | 1015 | .done: 1016 | rts ; Preserves condition codes 1017 | ; --------------------------------------------------------------------------- 1018 | .found_many: 1019 | tellp.l a1 ; Save output pointer 1020 | setcursor 1, 11 1021 | cmpi.b #4,d2 ; Did we have too many matches? 1022 | sgt.b d1 ; Set d1 if so 1023 | blt.s .make_index ; Branch if not 1024 | moveq #4,d2 ; Cap counter 1025 | 1026 | .make_index: 1027 | subq.w #2,d2 ; Discard one as it is in a2 and make it into a loop counter 1028 | 1029 | .print_candidates: 1030 | move.l (sp)+,d0 ; Fetch a stored address 1031 | move.w d2,-(sp) 1032 | bsr.w Print_Address 1033 | vtputc "," 1034 | move.w (sp)+,d2 1035 | dbra d2,.print_candidates 1036 | 1037 | move.l a2,d0 1038 | subq.l #2,d0 ; Was offset by 2 1039 | bsr.w Print_Address 1040 | 1041 | tst.b d1 ; Were there too many addresses? 1042 | beq.s .skip_dots ; Branch if not 1043 | vtputs ",..." 1044 | 1045 | .skip_dots: 1046 | seekset.l a1 ; Restore output pointer 1047 | move.b #%00010,ccr ; Overflow flag = "found too many" 1048 | rts 1049 | ; =========================================================================== 1050 | else 1051 | BusError: 1052 | AddressError: 1053 | TraceError: 1054 | SpuriousException: 1055 | ZeroDivideError: 1056 | CHKExceptionError: 1057 | TRAPVError: 1058 | IllegalInstrError: 1059 | PrivilegeViolation: 1060 | LineAEmulation: 1061 | LineFEmulation: 1062 | TrapVector: 1063 | .error_trap: 1064 | nop 1065 | nop 1066 | bra.s .error_trap 1067 | endif 1068 | 1069 | -------------------------------------------------------------------------------- /src/DebuggerMap.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flamewing/genesis-debugger/20fd1d8e21379b0a733a6b6ecc9a4fa8a3622d00/src/DebuggerMap.bin -------------------------------------------------------------------------------- /src/Disassembler.asm: -------------------------------------------------------------------------------- 1 | ; =========================================================================== 2 | ; Copyright (C) 2011-2018 by flamewing 3 | ; 4 | ; Permission to use, copy, modify, and/or distribute this software for any 5 | ; purpose with or without fee is hereby granted. 6 | ; 7 | ; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | ; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | ; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | ; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 13 | ; OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | ; =========================================================================== 15 | ; Alignment of disassembled instructions. 16 | DisassemblyAlign = 3 17 | ; =========================================================================== 18 | ; This should be the path to "Terminal.asm": 19 | include "Terminal.asm" 20 | ; =========================================================================== 21 | ; NO USER SERVICEABLE PARTS BELOW THIS POINT 22 | ; =========================================================================== 23 | ; SUBROUTINE 24 | ; Disassembles an opcode. 25 | ; This is the "bare bones" input needed by the disassembler; the function passed 26 | ; in (a4) may require more parameters than this. 27 | ; Also, the function in (a4) may affect the return guarantees of this function, 28 | ; even though it should strive to not do this. 29 | ; Input: 30 | ; a3 For normal, forward disassembly, a3 is PC after the opcode, but before 31 | ; any the extension words, if any; in this case, the function at (a4) must 32 | ; be a no-nop. 33 | ; For rewinding disassembly (address and bus errors), a3 is PC for either 34 | ; the next instruction to execute or, for move instructions only, it may 35 | ; be right before the destination extension word if the error happened 36 | ; when reading from the source. In this case, the function at (a4) should 37 | ; handle rewinding to just after the opcode for all instructions, that is, 38 | ; to the same location that would be in a3 in a forward disassembly. 39 | ; a4 Pointer to function that gets called once the size and effective address 40 | ; bits for the opcode are known. Its purpose is to rewind the PC to just 41 | ; before the first extension word if needed. 42 | ; a5 Where the output text goes 43 | ; d5 Instruction register 44 | ; Output: 45 | ; a2 PC after the instruction and all extension words 46 | ; a3 Program counter of instruction disassembled + 2 (before extension words) 47 | ; a5 Cell just after last printed text 48 | ; d4 Number of bytes taken by the instruction's extension words 49 | Disassemble_Opcode: 50 | moveq #0,d4 51 | ; Do a switch jump based on highest nibble of instruction. 52 | ; This matches broad categories laid out in programmer's reference. 53 | move.w d5,d0 54 | andi.w #$F000,d0 55 | rol.w #5,d0 56 | move.w OpCodeMap(pc,d0.w),d0 57 | jmp OpCodeMap(pc,d0.w) 58 | ; =========================================================================== 59 | OpCodeMap: offsetTable 60 | offsetTableEntry.w Dis_BitMan_MoveP_Immed ; %0000 61 | offsetTableEntry.w Dis_MoveByte ; %0001 62 | offsetTableEntry.w Dis_MoveLong ; %0010 63 | offsetTableEntry.w Dis_MoveWord ; %0011 64 | offsetTableEntry.w Dis_Miscellaneous ; %0100 65 | offsetTableEntry.w Dis_AddQ_SubQ_Scc_DBcc ; %0101 66 | offsetTableEntry.w Dis_Bcc_Bsr_Bra ; %0110 67 | offsetTableEntry.w Dis_MoveQ ; %0111 68 | offsetTableEntry.w Dis_Or_Div_Sbcd ; %1000 69 | offsetTableEntry.w Dis_Sub_SubX ; %1001 70 | offsetTableEntry.w Line1010Emulator ; %1010 71 | offsetTableEntry.w Dis_Cmp_Eor ; %1011 72 | offsetTableEntry.w Dis_And_Mul_Abcd_Exg ; %1100 73 | offsetTableEntry.w Dis_Add_AddX ; %1101 74 | offsetTableEntry.w Dis_Shift_Rotate ; %1110 75 | offsetTableEntry.w Line1111Emulator ; %1111 76 | ; =========================================================================== 77 | Dis_BitMan_MoveP_Immed: ; %0000 78 | ; Check to see if this is an immediate to CCR/SR instruction. 79 | move.w d5,d0 80 | andi.w #$5BF,d0 81 | cmpi.w #$3C,d0 82 | beq.w Handle_Immed_to_CCR_SR ; Branch if it is. 83 | ; Check to see if this is a bit manipulation instruction with an immediate 84 | ; operand. 85 | move.w d5,d0 86 | andi.w #$F00,d0 87 | cmpi.w #$800,d0 88 | beq.s Handle_BitMan_immed ; Branch if it is. 89 | ; Also check for MOVES, which does not exist on MC68000. 90 | cmpi.w #$E00,d0 91 | beq.w IllegalInstruction ; Branch if it is. 92 | ; Check to see if this is a reserved instruction (not on MC68000). 93 | move.w d5,d0 94 | andi.w #$1C0,d0 95 | cmpi.w #$C0,d0 96 | beq.w IllegalInstruction ; Branch if it is. 97 | ; Check to see if this is an instruction with immediate operands. 98 | btst #8,d5 99 | beq.w Handle_Immed ; Branch if it is. 100 | ; Check to see if this is a movep instruction. 101 | move.w d5,d0 102 | andi.w #$38,d0 103 | cmpi.w #$8,d0 104 | beq.w Handle_MoveP ; Branch if it is. 105 | ; Bit manipulation with bit number in register. 106 | move.w d5,d2 107 | andi.w #$C0,d2 108 | lsr.w #5,d2 109 | lea Bitman_Msgs(pc),a1 110 | adda.w (a1,d2.w),a1 111 | bsr.w Print_Message 112 | lea LongSize(pc),a1 113 | moveq #2,d3 ; Size of operation: long 114 | move.b d5,d1 115 | andi.b #$38,d1 116 | beq.s .got_size 117 | lea ByteSize(pc),a1 118 | moveq #0,d3 ; Size of operation: byte 119 | 120 | .got_size: 121 | bsr.w Print_Message 122 | move.w d5,d1 123 | andi.w #$E00,d1 124 | rol.w #7,d1 125 | bsr.w Reg_Data_Direct 126 | vtputc "," 127 | move.w d5,d1 ; Effective address bits 128 | moveq #0,d4 ; No additional immediate parameters 129 | jsr (a4) ; Rewind a3 to instruction+2 if needed 130 | lea (a3),a2 131 | bra.w Handle_effective_address 132 | ; =========================================================================== 133 | Handle_BitMan_immed: 134 | ; Bit manipulation with bit number in immediate value. 135 | move.w d5,d2 136 | andi.w #$C0,d2 137 | lsr.w #5,d2 138 | lea Bitman_Msgs(pc),a1 139 | adda.w (a1,d2.w),a1 140 | bsr.w Print_Message 141 | lea LongSize(pc),a1 142 | moveq #2,d3 ; Size of operation: long 143 | move.b d5,d1 144 | andi.b #$38,d1 145 | beq.s .got_size 146 | lea ByteSize(pc),a1 147 | moveq #0,d3 ; Size of operation: byte 148 | 149 | .got_size: 150 | bsr.w Print_Message 151 | 152 | move.w d5,d1 ; Effective address bits 153 | moveq #2,d4 ; Bit number is an immediate parameter 154 | jsr (a4) ; Rewind a3 to instruction+2 if needed 155 | lea (a3),a2 156 | move.w (a2)+,d0 ; Bit number in immediate field 157 | vtputc "#" 158 | bsr.w Print_Byte 159 | vtputc "," 160 | bra.w Handle_effective_address 161 | ; =========================================================================== 162 | Handle_MoveP: 163 | ; movep. 164 | lea MovePmsg(pc),a1 165 | bsr.w Print_Message 166 | lea WordSize(pc),a1 167 | moveq #1,d3 ; Size of operation: word 168 | btst #6,d5 169 | beq.s .got_size 170 | add.b d3,d3 171 | lea LongSize(pc),a1 172 | moveq #2,d3 ; Size of operation: long 173 | 174 | .got_size: 175 | bsr.w Print_Message 176 | ; Bit 7 indicates direction of operation. 177 | btst #7,d5 178 | beq.s .skip_reg 179 | ; Moving from register. 180 | move.w d5,d1 181 | andi.w #$E00,d1 182 | rol.w #7,d1 183 | bsr.w Reg_Data_Direct 184 | vtputc "," 185 | 186 | .skip_reg: 187 | move.w d5,d1 ; Effective address bits 188 | moveq #0,d4 ; No additional immediate parameters 189 | jsr (a4) ; Rewind a3 to instruction+2 if needed 190 | ; Print displacement (if nonzero). 191 | lea (a3),a2 192 | move.w (a2)+,d0 193 | beq.s .skip_null 194 | bsr.w Print_Word_Signed 195 | 196 | .skip_null: 197 | move.w d5,d1 198 | andi.w #7,d1 199 | moveq #2,d4 200 | btst #7,d5 201 | bne.w Reg_Addr_Indirect 202 | bsr.w Reg_Addr_Indirect 203 | ; If we got here, a register is the destination. 204 | vtputc "," 205 | move.w d5,d1 206 | andi.w #$E00,d1 207 | rol.w #7,d1 208 | bra.w Reg_Data_Direct 209 | ; =========================================================================== 210 | Handle_Immed: 211 | ; Instructions with immediate operands. 212 | move.w d5,d1 213 | andi.w #$E00,d1 214 | lsr.w #8,d1 215 | cmpi.b #8,d1 ; Invalid instruction? 216 | beq.w IllegalInstruction 217 | lea Immed_Msgs(pc),a1 218 | adda.w (a1,d1.w),a1 219 | bsr.w Print_Message 220 | move.w d5,d3 221 | andi.w #$C0,d3 222 | lsr.w #5,d3 223 | lea SizeMsgs(pc),a1 224 | adda.w (a1,d3.w),a1 225 | bsr.w Print_Message 226 | lsr.w #1,d3 227 | move.w d5,d1 ; Effective address bits 228 | moveq #2,d4 ; Immediate value is an additional parameter 229 | jsr (a4) ; Rewind a3 to instruction+2 if needed 230 | lea (a3),a2 231 | bsr.w Immediate 232 | vtputc "," 233 | bra.w Handle_effective_address 234 | ; =========================================================================== 235 | Handle_Immed_to_CCR_SR: 236 | ; Instructions affecting CCR/SR with immediate operands. 237 | move.w d5,d1 238 | andi.w #$E00,d1 239 | lsr.w #8,d1 240 | cmpi.b #8,d1 ; Invalid instruction? 241 | beq.w IllegalInstruction 242 | lea Immed_Msgs(pc),a1 243 | adda.w (a1,d1.w),a1 244 | bsr.w Print_Message 245 | move.w d5,d3 246 | andi.w #$C0,d3 247 | lsr.w #5,d3 248 | lea SizeMsgs(pc),a1 249 | adda.w (a1,d3.w),a1 250 | bsr.w Print_Message 251 | eori.w #2,d3 ; This is here because table is reused. 252 | lea Immed_to_CCR_SR_Msgs(pc),a1 253 | adda.w (a1,d3.w),a1 254 | lsr.w #1,d3 255 | move.w d5,d1 ; Effective address bits 256 | moveq #2,d4 ; Immediate is an additional parameter 257 | jsr (a4) ; Rewind a3 to instruction+2 if needed 258 | lea (a3),a2 259 | bsr.w Immediate 260 | vtputc "," 261 | bra.w Print_Message 262 | ; =========================================================================== 263 | Bitman_Msgs: offsetTable 264 | offsetTableEntry.w Btstmsg 265 | offsetTableEntry.w Bchgmsg 266 | offsetTableEntry.w Bclrmsg 267 | offsetTableEntry.w Bsetmsg 268 | Immed_Msgs: offsetTable 269 | offsetTableEntry.w Orimsg 270 | offsetTableEntry.w Andimsg 271 | offsetTableEntry.w Subimsg 272 | offsetTableEntry.w Addimsg 273 | offsetTableEntry.w IllegalInstructionMsg 274 | offsetTableEntry.w Eorimsg 275 | offsetTableEntry.w Cmpimsg 276 | offsetTableEntry.w IllegalInstructionMsg 277 | Immed_to_CCR_SR_Msgs: offsetTable 278 | offsetTableEntry.w SRegmsg 279 | offsetTableEntry.w CCRmsg 280 | offsetTableEntry.w CCRmsg 281 | offsetTableEntry.w SRegmsg 282 | Btstmsg: vtstring BLUE,"btst" 283 | Bchgmsg: vtstring BLUE,"bchg" 284 | Bclrmsg: vtstring BLUE,"bclr" 285 | Bsetmsg: vtstring BLUE,"bset" 286 | 287 | Orimsg: vtstring BLUE,"ori" 288 | Andimsg: vtstring BLUE,"andi" 289 | Subimsg: vtstring BLUE,"subi" 290 | Addimsg: vtstring BLUE,"addi" 291 | Eorimsg: vtstring BLUE,"eori" 292 | Cmpimsg: vtstring BLUE,"cmpi" 293 | 294 | SRegmsg: vtstring GREEN,"sr" 295 | CCRmsg: vtstring GREEN,"ccr" 296 | 297 | MovePmsg: vtstring BLUE,"movep" 298 | ; =========================================================================== 299 | Dis_MoveByte: ; %0001 300 | Dis_MoveWord: ; %0011 301 | Dis_MoveLong: ; %0010 302 | lea Movemsg(pc),a1 303 | bsr.w Print_Message 304 | move.w d5,d1 305 | lsr.w #3,d1 306 | move.w d1,d2 307 | andi.w #$38,d2 308 | lsr.w #6,d1 309 | andi.w #7,d1 310 | or.w d2,d1 ; Effective address bits for destination 311 | ; Is this a movea? 312 | cmpi.b #8,d2 313 | bne.s .not_addr ; Branch if not. 314 | vtputc "a",BLUE 315 | 316 | .not_addr: 317 | move.w d5,d3 318 | andi.w #$3000,d3 319 | rol.w #4,d3 320 | ; Remap move sizes to other sizes, so we can use the same table. 321 | move.b MoveSizeMsgs(pc,d3.w),d3 322 | lea SizeMsgs(pc),a1 323 | adda.w (a1,d3.w),a1 324 | bsr.w Print_Message 325 | move.w d1,-(sp) ; Save address mode of last parameter 326 | lsr.w #1,d3 327 | moveq #0,d4 ; No additional immediate parameters 328 | ; Note: for moves, the function should get the source effective address bits 329 | ; from the IR at d5. 330 | jsr (a4) ; Rewind a3 to instruction+2 if needed 331 | lea (a3),a2 332 | move.w d5,d1 333 | bsr.w Handle_effective_address 334 | vtputc "," 335 | move.w (sp)+,d0 336 | move.w d0,d1 337 | bra.w Handle_effective_address_Part2 338 | ; =========================================================================== 339 | Movemsg: vtstring BLUE,"move" 340 | MoveSizeMsgs: 341 | dc.b 6,0,4,2 342 | ; =========================================================================== 343 | Dis_Miscellaneous: ; %0100 344 | ; Chock full of unrelated instructions. 345 | ; Is this a lea or a chk instruction? 346 | btst #8,d5 347 | bne.w Handle_Lea_Chk ; Branch if yes. 348 | ; Is this a move to/from SR/CCR, negx, clr, neg or not instructions? 349 | btst #11,d5 350 | beq.w Handle_MoveSRCCR_NegX_Clr_Neg_Not ; Branch if yes. 351 | ; Otherwise, do a switch based on ramaining bits of second-to-highest nibble. 352 | move.w d5,d0 353 | andi.w #$600,d0 354 | lsr.w #8,d0 355 | move.w MiscOpCodeMap(pc,d0.w),d0 356 | jmp MiscOpCodeMap(pc,d0.w) 357 | ; =========================================================================== 358 | MiscOpCodeMap: offsetTable 359 | offsetTableEntry.w Handle_Ext_Nbcd_Swap_Pea_MoveMToMem ; %00 360 | offsetTableEntry.w Handle_Illegal_Tas_Tst ; %01 361 | offsetTableEntry.w Handle_MoveMToReg ; %10 362 | offsetTableEntry.w Handle_Trap_LinkW_Unlk_MoveUSP_Reset_Nop_Stop_Rte_Rts_TrapV_Rtr_Jmp_Jsr ; %11 363 | ; =========================================================================== 364 | Handle_Trap_LinkW_Unlk_MoveUSP_Reset_Nop_Stop_Rte_Rts_TrapV_Rtr_Jmp_Jsr: 365 | ; Select instruction based on second-to-lowest nibble. 366 | ; Is this a jmp or a jsr? 367 | btst #7,d5 368 | bne.w Handle_Jmp_Jsr ; Branch if yes. 369 | ;Is this a trap? 370 | move.w d5,d1 371 | andi.w #$30,d1 372 | beq.s Handle_Trap ; Branch if yes. 373 | ; Is this a link or unlk? 374 | cmpi.w #$10,d1 375 | beq.s Handle_Link_Unlk ; Branch if yes. 376 | ; Is this a move to/from USP? 377 | cmpi.w #$20,d1 378 | beq.w Handle_MoveUSP ; Branch if yes. 379 | ; Finally down to the last nibble! 380 | btst #3,d1 381 | bne.w IllegalInstruction 382 | ; Is this a stop? 383 | move.w d5,d1 384 | andi.w #7,d1 385 | cmpi.w #2,d1 386 | beq.s Handle_Stop ; Branch if yes. 387 | lsl.w #1,d1 388 | ; This is one of: reset, stop, rte, rts, trapv or rtr. 389 | ; None of them have operands. 390 | cmpi.b #8,d1 ; Invalid instruction? 391 | beq.w IllegalInstruction 392 | lea Reset_Nop_Stop_Rte_Rts_TrapV_Rtrmsgs(pc),a1 393 | adda.w (a1,d1.w),a1 394 | lea (a3),a2 395 | bra.w Print_Message 396 | ; =========================================================================== 397 | Handle_Stop: 398 | ; A stop instruction. 399 | lea Stopmsg(pc),a1 400 | bsr.w Print_Message 401 | moveq #1,d3 402 | lea (a3),a2 403 | bra.w Immediate 404 | ; =========================================================================== 405 | Handle_Trap: 406 | ; Watch out! A trap! 407 | lea Trapmsg(pc),a1 408 | bsr.w Print_Message 409 | move.w d5,d0 410 | andi.w #$F,d0 411 | lea (a3),a2 412 | bra.w Print_Byte 413 | ; =========================================================================== 414 | Handle_Link_Unlk: 415 | ; link or unlk. 416 | ; bit 3 says whether this is a link or unlk. 417 | btst #3,d5 418 | beq.s Handle_Link 419 | lea Unlkmsg(pc),a1 420 | bsr.w Print_Message 421 | move.w d5,d1 422 | andi.w #7,d1 423 | lea (a3),a2 424 | bra.w Reg_Addr_Direct 425 | ; =========================================================================== 426 | Handle_Link: 427 | lea Linkmsg(pc),a1 428 | bsr.w Print_Message 429 | move.w d5,d1 430 | andi.w #7,d1 431 | bsr.w Reg_Addr_Direct 432 | vtputc "," 433 | moveq #1,d3 434 | move.w d5,d1 ; Effective address bits 435 | moveq #2,d4 ; Number of bytes to allocate on stack is immediate 436 | jsr (a4) ; Rewind a3 to instruction+2 if needed 437 | lea (a3),a2 438 | bra.w Immediate 439 | ; =========================================================================== 440 | Handle_MoveUSP: 441 | ; Move to/from USP. 442 | lea Movemsg(pc),a1 443 | bsr.w Print_Message 444 | lea LongSize(pc),a1 445 | bsr.w Print_Message 446 | lea (a3),a2 447 | move.w d5,d1 448 | andi.w #7,d1 449 | ; Bit 3 indicated direction of move. 450 | btst #3,d5 451 | beq.s .to_usp 452 | ; Moving from USP. 453 | lea USPmsg(pc),a1 454 | bsr.w Print_Message 455 | vtputc "," 456 | bra.w Reg_Addr_Direct 457 | ; =========================================================================== 458 | .to_usp: 459 | bsr.w Reg_Addr_Direct 460 | ; Moving to USP. 461 | vtputc "," 462 | lea USPmsg(pc),a1 463 | bra.w Print_Message 464 | ; =========================================================================== 465 | Handle_Jmp_Jsr: 466 | ; jmp or jsr. 467 | ; Is this a jmp? 468 | lea Jmpmsg(pc),a1 469 | btst #6,d5 470 | bne.s .got_type ; Branch if so. 471 | lea Jsrmsg(pc),a1 472 | 473 | .got_type: 474 | bsr.w Print_Message 475 | move.w d5,d1 ; Effective address bits 476 | moveq #0,d4 ; No additional immediate parameters 477 | ; jsr (a4) will not return here if this was a jmp and we are in a rewinding 478 | ; disassembly that starts at the target of the jump. 479 | jsr (a4) ; Rewind a3 to instruction+2 if needed 480 | lea (a3),a2 481 | bra.w Handle_effective_address 482 | ; =========================================================================== 483 | Handle_Ext_Nbcd_Swap_Pea_MoveMToMem: 484 | ; ext, nbcd, swap, pea, movem to memory. 485 | ; If bit 7 is set, this can be an ext or movem. 486 | btst #7,d5 487 | beq.s .not_movem 488 | move.w d5,d1 489 | andi.w #$38,d1 490 | beq.s Handle_Ext ; This is an ext. 491 | bra.w Handle_MoveM ; We have a movem. 492 | ; =========================================================================== 493 | .not_movem: 494 | ; Filter out invalid instructions. 495 | move.w d5,d1 496 | andi.w #$38,d1 497 | cmpi.w #8,d1 498 | beq.w IllegalInstruction 499 | ; Is this an nbcd? 500 | btst #6,d5 501 | beq.s Handle_Nbcd ; Branch if yes. 502 | ; Check if this is a swap. 503 | tst.b d1 504 | beq.s Handle_Swap ; Branch if yes. 505 | ; We have a pea. Make soup? 506 | lea Peamsg(pc),a1 507 | bsr.w Print_Message 508 | moveq #2,d3 509 | move.w d5,d1 ; Effective address bits 510 | moveq #0,d4 ; No additional immediate parameters 511 | jsr (a4) ; Rewind a3 to instruction+2 if needed 512 | lea (a3),a2 513 | bra.w Handle_effective_address 514 | ; =========================================================================== 515 | Handle_Swap: 516 | ; Swap. 517 | lea Swapmsg(pc),a1 518 | bsr.w Print_Message 519 | move.w d5,d1 520 | andi.w #7,d1 521 | lea (a3),a2 522 | bra.w Reg_Data_Direct 523 | ; =========================================================================== 524 | Handle_Nbcd: 525 | ; Nbcd. 526 | lea Nbcdmsg(pc),a1 527 | bsr.w Print_Message 528 | moveq #0,d3 529 | move.w d5,d1 ; Effective address bits 530 | moveq #0,d4 ; No additional immediate parameters 531 | jsr (a4) ; Rewind a3 to instruction+2 if needed 532 | lea (a3),a2 533 | bra.w Handle_effective_address 534 | ; =========================================================================== 535 | Handle_Ext: 536 | ; Ext. 537 | lea Extmsg(pc),a1 538 | bsr.w Print_Message 539 | lea WordSize(pc),a1 540 | btst #6,d5 541 | beq.s .got_size 542 | lea LongSize(pc),a1 543 | 544 | .got_size: 545 | bsr.w Print_Message 546 | move.w d5,d1 547 | andi.w #7,d1 548 | lea (a3),a2 549 | bra.w Reg_Data_Direct 550 | ; =========================================================================== 551 | Handle_MoveMToReg: 552 | ; Bit 7 is another instruction that doesn't exist for MC68000. 553 | btst #7,d5 554 | beq.w IllegalInstruction 555 | 556 | Handle_MoveM: 557 | ; Movem. 558 | ; This is for an end-of-line marker, as movem instructions can give quite 559 | ; long lines when disassembled. 560 | tellp.w d0 561 | seekp.w nCols-DisassemblyAlign-1,d0 562 | move.w d0,-(sp) 563 | lea MoveMmsg(pc),a1 564 | bsr.w Print_Message 565 | lea WordSize(pc),a1 566 | moveq #1,d3 567 | btst #6,d5 568 | beq.s .got_size 569 | lea LongSize(pc),a1 570 | add.w d3,d3 571 | 572 | .got_size: 573 | bsr.w Print_Message 574 | 575 | move.w d5,d1 ; Effective address bits 576 | moveq #2,d4 ; Register list is an additional immediate parameter 577 | jsr (a4) ; Rewind a3 to instruction+2 if needed 578 | 579 | ; a0 = end-of-line address. 580 | movea.w (sp)+,a0 581 | lea (a3),a2 582 | ; Fetch list. 583 | move.w (a2)+,d0 584 | ; Assuming normal register list. 585 | lea Normal_Next_Reg(pc),a4 586 | ; If bit 10 is set, we are moving from register list to memory. 587 | btst #10,d5 588 | bne.s .reg_to_mem 589 | move.w d5,d1 590 | andi.w #$38,d1 591 | ; Is the addressing move -(An)? 592 | cmpi.w #$20,d1 593 | bne.s .not_predecr 594 | ; It is; use reversed register list. 595 | lea Predecr_Next_Reg(pc),a4 596 | 597 | .not_predecr: 598 | ; Print register list. 599 | bsr.s Handle_MoveM_Reg_List 600 | vtputc "," 601 | bsr.s Do_Line_Break 602 | bra.w Handle_effective_address 603 | ; =========================================================================== 604 | .reg_to_mem: 605 | move.w d0,-(sp) 606 | bsr.w Handle_effective_address 607 | move.w (sp)+,d0 608 | ; Moving to register list; print it. 609 | vtputc "," 610 | bra.s Handle_MoveM_Reg_List 611 | ; =========================================================================== 612 | Normal_Next_Reg: 613 | lsr.w #1,d0 614 | rts 615 | ; --------------------------------------------------------------------------- 616 | Normal_Put_Back: 617 | roxl.w #1,d0 618 | rts 619 | ; =========================================================================== 620 | Predecr_Next_Reg: 621 | lsl.w #1,d0 622 | rts 623 | ; --------------------------------------------------------------------------- 624 | if *-Predecr_Next_Reg > Normal_Put_Back-Normal_Next_Reg 625 | fatal "The code in Predecr_Next_Reg must not be larger than the code in Normal_Next_Reg" 626 | endif 627 | org Predecr_Next_Reg+Normal_Put_Back-Normal_Next_Reg 628 | Predecr_Put_Back: 629 | roxr.w #1,d0 630 | rts 631 | ; =========================================================================== 632 | Handle_MoveM_Reg_List: 633 | ; Prints a register list for -(An), adding line breaks if needed. 634 | ; First, do Dn. 635 | moveq #15,d3 ; 16 registers 636 | 637 | ; Loop until a register is specified. 638 | .find_reg_loop: 639 | jsr (a4) 640 | dblo.w d3,.find_reg_loop 641 | ; Save the register. 642 | move.w d3,d2 643 | bmi.s .done ; We finished the loop, so exit. 644 | 645 | ; Loop through all contiguous registers. 646 | .find_last_reg: 647 | jsr (a4) 648 | dbhs.w d3,.find_last_reg 649 | 650 | ; Add back last bit shifted out. 651 | jsr Normal_Put_Back-Normal_Next_Reg(a4) 652 | ; Have we finished going through the list? 653 | tst.w d3 654 | bpl.s .list_not_done 655 | ; Yes; make d3 sane. 656 | addq.w #1,d3 657 | 658 | .list_not_done: 659 | ; Do we need to print one or more registers? 660 | cmp.w d3,d2 661 | beq.s .do_single_reg 662 | ; More than one. Print first register. 663 | moveq #15,d1 664 | sub.w d2,d1 665 | bsr.s Print_Reg 666 | vtputc "-" 667 | 668 | .do_single_reg: 669 | ; Print register. 670 | moveq #15,d1 671 | sub.w d3,d1 672 | bsr.s Print_Reg 673 | ; Have we finished going through the list? 674 | tst.w d0 675 | beq.s .done 676 | ; No. Print a slash and loop. 677 | vtputc "/" 678 | bsr.s Do_Line_Break 679 | dbra d3,.find_reg_loop 680 | 681 | .done: 682 | rts 683 | ; =========================================================================== 684 | Print_Reg: 685 | cmpi.b #8,d1 686 | blo.w Reg_Data_Direct 687 | subq.w #8,d1 688 | bra.w Reg_Addr_Direct 689 | ; =========================================================================== 690 | Do_Line_Break: 691 | ; Prints a register list for all but -(An), adding line breaks if needed. 692 | move.w a0,d1 693 | getdst.w d1 694 | cmpi.w #8,d1 695 | bhs.s .done 696 | 697 | moveq #' ',d2 698 | subq.w #2,d1 ; Make it into a loop counter and remove an additional character 699 | bmi.s .buffer_underrun ; This should never happen 700 | 701 | fillr d2,d1 702 | vtputs "\\ " ; Put a line continuation character and a blank 703 | 704 | .buffer_underrun: 705 | moveq #9+DisassemblyAlign,d1 706 | fillr d2,d1 707 | adda.w #DisplayCell(0, 1),a0 708 | 709 | .done: 710 | rts 711 | ; =========================================================================== 712 | Handle_Illegal_Tas_Tst: 713 | ; illegal, tas, tst. 714 | ; Is this a tst? 715 | move.w d5,d1 716 | andi.w #$C0,d1 717 | cmpi.w #$C0,d1 718 | bne.s Handle_Tst ; Branch if yes. 719 | ; Is this an invalid instruction? 720 | move.w d5,d1 721 | andi.w #$3F,d1 722 | cmpi.w #$3A,d1 723 | beq.w IllegalInstruction ; If yes, branch. 724 | ; Is this a illegal instruction? 725 | cmpi.w #$3C,d1 726 | beq.s Handle_Illegal ; Branch if yes. 727 | ; We have a tas. 728 | lea Tasmsg(pc),a1 729 | bsr.w Print_Message 730 | moveq #0,d3 731 | move.w d5,d1 ; Effective address bits 732 | moveq #0,d4 ; No additional immediate parameters 733 | jsr (a4) ; Rewind a3 to instruction+2 if needed 734 | lea (a3),a2 735 | bra.w Handle_effective_address 736 | ; =========================================================================== 737 | Handle_Illegal: 738 | ; illegal. 739 | lea Illegalmsg(pc),a1 740 | lea (a3),a2 741 | bra.w Print_Message 742 | ; =========================================================================== 743 | Handle_Tst: 744 | ; tst. 745 | lea Tstmsg(pc),a1 746 | bra.s Handle_NegX_Clr_Neg_Not_Tst 747 | ; =========================================================================== 748 | Handle_MoveSRCCR_NegX_Clr_Neg_Not: 749 | ; Is this a move to/from SR/CCR? 750 | move.w d5,d1 751 | andi.w #$C0,d1 752 | cmpi.w #$C0,d1 753 | beq.s Handle_MoveSRCCR ; Branch if yes. 754 | ; negx, clr, neg, not. 755 | move.w d5,d1 756 | andi.w #$600,d1 757 | lsr.w #8,d1 758 | lea NegX_Clr_Neg_Notmsgs(pc),a1 759 | adda.w (a1,d1.w),a1 760 | 761 | Handle_NegX_Clr_Neg_Not_Tst: 762 | bsr.w Print_Message 763 | 764 | move.w d5,d3 765 | andi.w #$C0,d3 766 | lsr.w #5,d3 767 | lea SizeMsgs(pc),a1 768 | adda.w (a1,d3.w),a1 769 | bsr.w Print_Message 770 | lsr.w #1,d3 771 | move.w d5,d1 ; Effective address bits 772 | moveq #0,d4 ; No additional immediate parameters 773 | jsr (a4) ; Rewind a3 to instruction+2 if needed 774 | lea (a3),a2 775 | bra.w Handle_effective_address 776 | ; =========================================================================== 777 | Handle_MoveSRCCR: 778 | ; Move to/from SR/CCR. 779 | lea Movemsg(pc),a1 780 | bsr.w Print_Message 781 | lea WordSize(pc),a1 782 | bsr.w Print_Message 783 | moveq #1,d3 784 | 785 | move.w d5,d1 ; Effective address bits 786 | moveq #0,d4 ; No additional immediate parameters 787 | jsr (a4) ; Rewind a3 to instruction+2 if needed 788 | lea (a3),a2 789 | btst #10,d5 790 | beq.s .from_ccr_sr 791 | ; Move is to SR/CCR. 792 | bsr.w Handle_effective_address 793 | vtputc "," 794 | 795 | .from_ccr_sr: 796 | move.w d5,d1 797 | andi.w #$600,d1 798 | lsr.w #8,d1 799 | lea Immed_to_CCR_SR_Msgs(pc),a1 800 | adda.w (a1,d1.w),a1 801 | btst #10,d5 802 | bne.w Print_Message 803 | bsr.w Print_Message 804 | ; Move is from SR/CCR. 805 | vtputc "," 806 | bra.w Handle_effective_address 807 | ; =========================================================================== 808 | Handle_Lea_Chk: 809 | ; lea, chk. 810 | ; Is this an invalid instruction on MC68000? 811 | move.w d5,d1 812 | andi.w #$1C0,d1 813 | cmpi.w #$1C0,d1 814 | bne.s .not_invalid ; Branch if not. 815 | move.w d5,d1 816 | andi.w #$38,d1 817 | beq.w IllegalInstruction ; Branch if yes. 818 | 819 | .not_invalid: 820 | ; Is this a chk? 821 | btst #6,d5 822 | beq.s .chk ; Branch if yes. 823 | ; lea. 824 | lea Leamsg(pc),a1 825 | bsr.w Print_Message 826 | moveq #2,d3 827 | bra.s .lea_chk 828 | ; =========================================================================== 829 | .chk: 830 | ; chk. 831 | btst #7,d5 832 | beq.w IllegalInstruction 833 | lea Chkmsg(pc),a1 834 | bsr.w Print_Message 835 | moveq #1,d3 836 | 837 | .lea_chk: 838 | move.w d5,d1 ; Effective address bits 839 | moveq #0,d4 ; No additional immediate parameters 840 | jsr (a4) ; Rewind a3 to instruction+2 if needed 841 | lea (a3),a2 842 | bsr.w Handle_effective_address 843 | vtputc "," 844 | move.w d5,d1 845 | andi.w #$E00,d1 846 | rol.w #7,d1 847 | btst #6,d5 848 | beq.w Reg_Data_Direct 849 | bra.w Reg_Addr_Direct 850 | ; =========================================================================== 851 | NegX_Clr_Neg_Notmsgs: offsetTable 852 | offsetTableEntry.w NegXmsg 853 | offsetTableEntry.w Clrmsg 854 | offsetTableEntry.w Negmsg 855 | offsetTableEntry.w Notmsg 856 | NegXmsg: vtstring BLUE,"negx" 857 | Clrmsg: vtstring BLUE,"clr" 858 | Negmsg: vtstring BLUE,"neg" 859 | Notmsg: vtstring BLUE,"not" 860 | 861 | Reset_Nop_Stop_Rte_Rts_TrapV_Rtrmsgs: offsetTable 862 | offsetTableEntry.w Resetmsg 863 | offsetTableEntry.w Nopmsg 864 | offsetTableEntry.w Stopmsg 865 | offsetTableEntry.w Rtemsg 866 | offsetTableEntry.w IllegalInstructionMsg 867 | offsetTableEntry.w Rtsmsg 868 | offsetTableEntry.w TrapVmsg 869 | offsetTableEntry.w Rtrmsg 870 | Resetmsg: vtstring BLUE,"reset" 871 | Nopmsg: vtstring BLUE,"nop" 872 | Rtemsg: vtstring BLUE,"rte" 873 | Rtsmsg: vtstring BLUE,"rts" 874 | TrapVmsg: vtstring BLUE,"trapv" 875 | Rtrmsg: vtstring BLUE,"rtr" 876 | 877 | Nbcdmsg: vtstring BLUE,"nbcd.b " 878 | Extmsg: vtstring BLUE,"ext" 879 | Swapmsg: vtstring BLUE,"swap " 880 | MoveMmsg: vtstring BLUE,"movem" 881 | 882 | Stopmsg: vtstring BLUE,"stop " 883 | USPmsg: vtstring GREEN,"usp" 884 | Linkmsg: vtstring BLUE,"link " 885 | Unlkmsg: vtstring BLUE,"unlk " 886 | Trapmsg: vtstring BLUE,"trap #" 887 | Jmpmsg: vtstring BLUE,"jmp " 888 | Jsrmsg: vtstring BLUE,"jsr " 889 | Leamsg: vtstring BLUE,"lea " 890 | Peamsg: vtstring BLUE,"pea " 891 | Chkmsg: vtstring BLUE,"chk.w " 892 | Tstmsg: vtstring BLUE,"tst" 893 | Tasmsg: vtstring BLUE,"tas.b " 894 | Illegalmsg: vtstring BLUE,"illegal" 895 | ; =========================================================================== 896 | Dis_AddQ_SubQ_Scc_DBcc: ; %0101 897 | ; Is this addq or subq? 898 | move.w d5,d1 899 | andi.w #$C0,d1 900 | cmpi.w #$C0,d1 901 | bne.w Handle_AddQ_SubQ ; Branch if yes. 902 | ; Is this a loop instruction (DBcc)? 903 | move.w d5,d1 904 | andi.w #$38,d1 905 | cmpi.w #8,d1 906 | beq.s Handle_Dbcc ; Branch if yes. 907 | ; Is this Scc? 908 | cmpi.w #$38,d1 909 | bne.w Handle_Scc ; Branch if yes. 910 | move.w d5,d1 911 | andi.w #6,d1 912 | beq.w Handle_Scc ; Branch if yes. 913 | bra.w IllegalInstruction 914 | ; =========================================================================== 915 | Handle_Dbcc: 916 | ; DBcc. 917 | lea DbCCmsg(pc),a1 918 | bsr.w Print_Message 919 | move.w d5,d2 920 | andi.w #$F00,d2 921 | lsr.w #7,d2 922 | lea Db_Trap_S_CC(pc),a1 923 | adda.w (a1,d2.w),a1 924 | bsr.w Print_Message 925 | lea WordSize(pc),a1 926 | bsr.w Print_Message 927 | move.w d5,d1 928 | andi.w #7,d1 929 | bsr.w Reg_Data_Direct 930 | vtputc "," 931 | moveq #1,d3 ; Size = word; irrelevant for this opcode 932 | move.w d5,d1 ; Effective address bits; irrelevant here 933 | moveq #2,d4 ; Branch distance is an additional immediate parameter 934 | ; Note: rewinding disassemblies do not return from jsr (a4) if they started 935 | ; at the target of the dbCC. 936 | jsr (a4) ; Rewind a3 to instruction+2 if needed 937 | move.w (a3),d0 938 | ext.l d0 939 | add.l a3,d0 940 | lea 2(a3),a2 941 | bra.w Print_Address 942 | ; =========================================================================== 943 | Handle_Scc: 944 | ; Scc. 945 | vtputc "s",BLUE 946 | move.w d5,d2 947 | andi.w #$F00,d2 948 | lsr.w #7,d2 949 | lea Db_Trap_S_CC(pc),a1 950 | adda.w (a1,d2.w),a1 951 | bsr.w Print_Message 952 | lea ByteSize(pc),a1 953 | bsr.w Print_Message 954 | lea (a3),a2 955 | bra.w Handle_effective_address 956 | ; =========================================================================== 957 | Handle_AddQ_SubQ: 958 | ; addq/subq. 959 | lea AddQmsg(pc),a1 960 | btst #8,d5 961 | beq.s .got_msg 962 | lea SubQmsg(pc),a1 963 | 964 | .got_msg: 965 | bsr.w Print_Message 966 | move.w d5,d3 967 | andi.w #$C0,d3 968 | lsr.w #5,d3 969 | lea SizeMsgs(pc),a1 970 | adda.w (a1,d3.w),a1 971 | bsr.w Print_Message 972 | lsr.w #1,d3 973 | vtputc "#" 974 | move.w d5,d2 975 | andi.w #$E00,d2 976 | rol.w #7,d2 977 | bne.s .got_immed 978 | addq.w #8,d2 979 | 980 | .got_immed: 981 | itoa.w d2,RED 982 | vtput.w d2 983 | vtputc "," 984 | lea (a3),a2 985 | bra.w Handle_effective_address 986 | ; =========================================================================== 987 | AddQmsg: vtstring BLUE,"addq" 988 | SubQmsg: vtstring BLUE,"subq" 989 | DbCCmsg: vtstring BLUE,"db" 990 | ; =========================================================================== 991 | Db_Trap_S_CC: offsetTable 992 | offsetTableEntry.w Tcmsg 993 | offsetTableEntry.w Fcmsg 994 | offsetTableEntry.w HImsg 995 | offsetTableEntry.w LSmsg 996 | offsetTableEntry.w CCmsg 997 | offsetTableEntry.w CSmsg 998 | offsetTableEntry.w NEmsg 999 | offsetTableEntry.w EQmsg 1000 | offsetTableEntry.w VCmsg 1001 | offsetTableEntry.w VSmsg 1002 | offsetTableEntry.w PLmsg 1003 | offsetTableEntry.w MImsg 1004 | offsetTableEntry.w GEmsg 1005 | offsetTableEntry.w LTmsg 1006 | offsetTableEntry.w GTmsg 1007 | offsetTableEntry.w LEmsg 1008 | ; =========================================================================== 1009 | Dis_Bcc_Bsr_Bra: ; %0110 1010 | ; Bcc, BSR, BRA. 1011 | vtputc "b",BLUE 1012 | move.w d5,d2 1013 | andi.w #$F00,d2 1014 | lsr.w #7,d2 1015 | lea Branch_CC(pc),a1 1016 | adda.w (a1,d2.w),a1 1017 | bsr.w Print_Message 1018 | tst.b d5 ; Is this a short branch? 1019 | sne.b d3 ; d3 = -1 if yes 1020 | lea ShortSize(pc),a1 1021 | bne.s .got_size ; Branch if short jump. 1022 | lea WordSize(pc),a1 1023 | 1024 | .got_size: 1025 | bsr.w Print_Message 1026 | addq.b #1,d3 ; d3 = 0 for short branch, 1 for word branch 1027 | move.w d5,d1 ; Effective address bits; irrelevant for bCC 1028 | moveq #0,d4 ; No additional immediate parameters 1029 | ; Note: rewinding disassemblies may not return from jsr (a4) if they started 1030 | ; at the target of the bCC; the exception would be for a bsr, for which the 1031 | ; return address is available, hence the instruction's PC is recoverable. 1032 | jsr (a4) ; Rewind a3 to instruction+2 if needed 1033 | tst.b d5 ; Is this a short branch? 1034 | bne.s Handle_Bcc_Short ; Branch if yes. 1035 | move.w (a3),d0 1036 | ext.l d0 1037 | add.l a3,d0 1038 | moveq #2,d4 1039 | lea 2(a3),a2 1040 | bra.w Print_Address 1041 | ; =========================================================================== 1042 | Handle_Bcc_Short: 1043 | move.b d5,d0 1044 | ext.w d0 1045 | ext.l d0 1046 | add.l a3,d0 1047 | lea (a3),a2 1048 | bra.w Print_Address 1049 | ; =========================================================================== 1050 | Branch_CC: offsetTable 1051 | offsetTableEntry.w RAmsg 1052 | offsetTableEntry.w SRmsg 1053 | offsetTableEntry.w HImsg 1054 | offsetTableEntry.w LSmsg 1055 | offsetTableEntry.w CCmsg 1056 | offsetTableEntry.w CSmsg 1057 | offsetTableEntry.w NEmsg 1058 | offsetTableEntry.w EQmsg 1059 | offsetTableEntry.w VCmsg 1060 | offsetTableEntry.w VSmsg 1061 | offsetTableEntry.w PLmsg 1062 | offsetTableEntry.w MImsg 1063 | offsetTableEntry.w GEmsg 1064 | offsetTableEntry.w LTmsg 1065 | offsetTableEntry.w GTmsg 1066 | offsetTableEntry.w LEmsg 1067 | Tcmsg: vtstring BLUE,"t" 1068 | Fcmsg: vtstring BLUE,"f" 1069 | RAmsg: vtstring BLUE,"ra" 1070 | SRmsg: vtstring BLUE,"sr" 1071 | HImsg: vtstring BLUE,"hi" 1072 | LSmsg: vtstring BLUE,"ls" 1073 | CCmsg: vtstring BLUE,"cc" 1074 | CSmsg: vtstring BLUE,"cs" 1075 | NEmsg: vtstring BLUE,"ne" 1076 | EQmsg: vtstring BLUE,"eq" 1077 | VCmsg: vtstring BLUE,"vc" 1078 | VSmsg: vtstring BLUE,"vs" 1079 | PLmsg: vtstring BLUE,"pl" 1080 | MImsg: vtstring BLUE,"mi" 1081 | GEmsg: vtstring BLUE,"ge" 1082 | LTmsg: vtstring BLUE,"lt" 1083 | GTmsg: vtstring BLUE,"gt" 1084 | LEmsg: vtstring BLUE,"le" 1085 | ; =========================================================================== 1086 | Dis_MoveQ: ; %0111 1087 | ; moveq. 1088 | lea Moveqmsg(pc),a1 1089 | bsr.w Print_Message 1090 | move.w d5,d0 1091 | bsr.w Print_Byte_Signed 1092 | vtputc "," 1093 | move.w d5,d1 1094 | andi.w #$E00,d1 1095 | rol.w #7,d1 1096 | lea (a3),a2 1097 | bra.w Reg_Data_Direct 1098 | ; =========================================================================== 1099 | Moveqmsg: vtstring BLUE,"moveq #" 1100 | ; =========================================================================== 1101 | Dis_Or_Div_Sbcd: ; %1000 1102 | ; or, div, sbcd. 1103 | ; Is this a div? 1104 | move.w d5,d1 1105 | andi.w #$C0,d1 1106 | cmpi.w #$C0,d1 1107 | beq.w Handle_Div ; Branch if yes. 1108 | ; Filter out invalid instructions. 1109 | move.w d5,d1 1110 | andi.w #$1F0,d1 1111 | cmpi.w #$180,d1 1112 | beq.w IllegalInstruction 1113 | cmpi.w #$140,d1 1114 | beq.w IllegalInstruction 1115 | ; Is this a sbcd? 1116 | andi.w #$1F0,d1 1117 | cmpi.w #$100,d1 1118 | beq.s Handle_Sbcd ; Branch if yes. 1119 | lea Ormsg(pc),a1 1120 | bsr.w Print_Message 1121 | ; or. 1122 | move.w d5,d3 1123 | andi.w #$C0,d3 1124 | lsr.w #5,d3 1125 | bra.w Handle_Add_And_Or_Sub 1126 | ; =========================================================================== 1127 | Handle_Sbcd: 1128 | ; sbcd. 1129 | lea Sbcdmsg(pc),a1 1130 | bsr.w Print_Message 1131 | moveq #0,d3 1132 | bra.w Handle_AddX_Abcd_SubX_Sbcd_Common 1133 | ; =========================================================================== 1134 | Handle_Div: 1135 | ; div. 1136 | lea Divmsg(pc),a1 1137 | bra.w Handle_Div_Mul_Common 1138 | ; =========================================================================== 1139 | Sbcdmsg: vtstring BLUE,"sbcd.b " 1140 | Ormsg: vtstring BLUE,"or" 1141 | Divmsg: vtstring BLUE,"div" 1142 | ; =========================================================================== 1143 | Dis_Cmp_Eor: ; %1011 1144 | ; cmp, eor. 1145 | ; Is this a cmpm? 1146 | move.w d5,d1 1147 | andi.w #$138,d1 1148 | cmpi.w #$108,d1 1149 | beq.w Handle_CmpM ; Branch if yes. 1150 | ; Is this a cmpa? 1151 | move.w d5,d1 1152 | andi.w #$C0,d1 1153 | cmpi.w #$C0,d1 1154 | beq.s Handle_CmpA ; Branch if yes. 1155 | ; Is this a normal cmp? 1156 | btst #8,d5 1157 | beq.s Handle_Cmp ; Branch if yes. 1158 | ; eor. 1159 | lea Eormsg(pc),a1 1160 | bsr.w Print_Message 1161 | move.w d5,d3 1162 | andi.w #$C0,d3 1163 | lsr.w #5,d3 1164 | lea SizeMsgs(pc),a1 1165 | adda.w (a1,d3.w),a1 1166 | bsr.w Print_Message 1167 | lsr.w #1,d3 1168 | move.w d5,d1 1169 | andi.w #$E00,d1 1170 | rol.w #7,d1 1171 | bsr.w Reg_Data_Direct 1172 | vtputc "," 1173 | move.w d5,d1 ; Effective address bits 1174 | moveq #0,d4 ; No additional immediate parameters 1175 | jsr (a4) ; Rewind a3 to instruction+2 if needed 1176 | lea (a3),a2 1177 | bra.w Handle_effective_address 1178 | ; =========================================================================== 1179 | Handle_Cmp: 1180 | ; cmp. 1181 | lea Cmpmsg(pc),a1 1182 | pea Reg_Data_Direct(pc) ; An rts will branch to this now 1183 | bsr.w Print_Message 1184 | move.w d5,d3 1185 | andi.w #$C0,d3 1186 | lsr.w #5,d3 1187 | bra.s Handle_Cmp_CmpA 1188 | ; =========================================================================== 1189 | Handle_CmpA: 1190 | ; cmpa. 1191 | lea CmpAmsg(pc),a1 1192 | pea Reg_Addr_Direct(pc) ; An rts will branch to this now 1193 | bsr.w Print_Message 1194 | moveq #2,d3 1195 | btst #8,d5 1196 | beq.s .got_size 1197 | add.b d3,d3 1198 | 1199 | .got_size: 1200 | Handle_Cmp_CmpA: 1201 | lea SizeMsgs(pc),a1 1202 | adda.w (a1,d3.w),a1 1203 | bsr.w Print_Message 1204 | lsr.w #1,d3 1205 | move.w d5,d1 ; Effective address bits 1206 | moveq #0,d4 ; No additional immediate parameters 1207 | jsr (a4) ; Rewind a3 to instruction+2 if needed 1208 | lea (a3),a2 1209 | bsr.w Handle_effective_address 1210 | vtputc "," 1211 | move.w d5,d1 1212 | andi.w #$E00,d1 1213 | rol.w #7,d1 1214 | rts ; Branches to either Reg_Data_Direct or Reg_Addr_Direct 1215 | ; =========================================================================== 1216 | Handle_CmpM: 1217 | ; cmpm. 1218 | lea CmpMmsg(pc),a1 1219 | bsr.w Print_Message 1220 | move.w d5,d3 1221 | andi.w #$C0,d3 1222 | lsr.w #5,d3 1223 | lea SizeMsgs(pc),a1 1224 | adda.w (a1,d3.w),a1 1225 | bsr.w Print_Message 1226 | move.w d5,d1 1227 | andi.w #7,d1 1228 | bsr.w Reg_Addr_Post_Indirect 1229 | move.w d5,d1 1230 | andi.w #$E00,d1 1231 | rol.w #7,d1 1232 | lea (a3),a2 1233 | bra.w Reg_Addr_Post_Indirect 1234 | ; =========================================================================== 1235 | Eormsg: vtstring BLUE,"eor" 1236 | Cmpmsg: vtstring BLUE,"cmp" 1237 | CmpAmsg: vtstring BLUE,"cmpa" 1238 | CmpMmsg: vtstring BLUE,"cmpm" 1239 | ; =========================================================================== 1240 | Dis_And_Mul_Abcd_Exg: ; %1100 1241 | ; and, mul, abcd, exg. 1242 | ; Is this a mul? 1243 | move.w d5,d1 1244 | andi.w #$C0,d1 1245 | cmpi.w #$C0,d1 1246 | beq.w Handle_Mul ; Branch if so. 1247 | ; Is this an exg? 1248 | move.w d5,d1 1249 | andi.w #$1F8,d1 1250 | cmpi.w #$188,d1 1251 | beq.s Handle_Exg_Mixed ; Branch if yes. 1252 | cmpi.w #$148,d1 1253 | beq.s Handle_Exg_Both_Addr ; Branch if yes. 1254 | cmpi.w #$140,d1 1255 | beq.s Handle_Exg_Both_Data ; Branch if yes. 1256 | ; Is this an abcd? 1257 | andi.w #$1F0,d1 1258 | cmpi.w #$100,d1 1259 | beq.s Handle_Abcd ; Branch if yes. 1260 | ; and. 1261 | lea Andmsg(pc),a1 1262 | bsr.w Print_Message 1263 | move.w d5,d3 1264 | andi.w #$C0,d3 1265 | lsr.w #5,d3 1266 | bra.w Handle_Add_And_Or_Sub 1267 | ; =========================================================================== 1268 | Handle_Exg_Both_Data: 1269 | ; exg dx,dy. 1270 | lea Reg_Data_Direct(pc),a2 1271 | pea Reg_Data_Direct(pc) ; An rts will branch to this now 1272 | bra.s Handle_Exg 1273 | ; =========================================================================== 1274 | Handle_Exg_Both_Addr: 1275 | ; exg ax,ay. 1276 | lea Reg_Addr_Indirect(pc),a2 1277 | pea Reg_Addr_Indirect(pc) ; An rts will branch to this now 1278 | bra.s Handle_Exg 1279 | ; =========================================================================== 1280 | Handle_Exg_Mixed: 1281 | ; exg dx,ay. 1282 | lea Reg_Data_Direct(pc),a2 1283 | pea Reg_Addr_Indirect(pc) ; An rts will branch to this now 1284 | 1285 | Handle_Exg: 1286 | ; exg. 1287 | lea Exgmsg(pc),a1 1288 | bsr.w Print_Message 1289 | move.w d5,d1 1290 | andi.w #$E00,d1 1291 | rol.w #7,d1 1292 | jsr (a2) 1293 | vtputc "," 1294 | move.w d5,d1 1295 | andi.w #7,d1 1296 | lea (a3),a2 1297 | rts ; Branches to Reg_(Data_D|Addr_Ind)irect 1298 | ; =========================================================================== 1299 | Handle_Abcd: 1300 | ; The alphabet. 1301 | lea Abcdmsg(pc),a1 1302 | bsr.w Print_Message 1303 | moveq #0,d3 1304 | bra.w Handle_AddX_Abcd_SubX_Sbcd_Common 1305 | ; =========================================================================== 1306 | Handle_Mul: 1307 | ; A mul? Is this Dark Sun? 1308 | lea Mulmsg(pc),a1 1309 | 1310 | Handle_Div_Mul_Common: 1311 | ; Div/mul. 1312 | ; Print u or s as needed. 1313 | bsr.w Print_Message 1314 | 1315 | btst #8,d5 1316 | beq.s .is_unsigned 1317 | vtputc "s",BLUE 1318 | bra.s .got_signedness 1319 | ; --------------------------------------------------------------------------- 1320 | .is_unsigned: 1321 | vtputc "u",BLUE 1322 | 1323 | .got_signedness: 1324 | lea WordSize(pc),a1 1325 | bsr.w Print_Message 1326 | moveq #1,d3 ; Word sized 1327 | move.w d5,d1 ; Effective address bits 1328 | moveq #0,d4 ; No additional immediate parameters 1329 | jsr (a4) ; Rewind a3 to instruction+2 if needed 1330 | lea (a3),a2 1331 | bsr.w Handle_effective_address 1332 | vtputc "," 1333 | move.w d5,d1 1334 | andi.w #$E00,d1 1335 | rol.w #7,d1 1336 | bra.w Reg_Data_Direct 1337 | ; =========================================================================== 1338 | Abcdmsg: vtstring BLUE,"abcd.b " 1339 | Andmsg: vtstring BLUE,"and" 1340 | Exgmsg: vtstring BLUE,"exg.l " 1341 | Mulmsg: vtstring BLUE,"mul" 1342 | ; =========================================================================== 1343 | Dis_Sub_SubX: ; %1001 1344 | ; sub, subx. 1345 | lea Submsg(pc),a1 1346 | bra.s Handle_Add_AddX_Sub_SubX 1347 | ; =========================================================================== 1348 | Dis_Add_AddX: ; %1101 1349 | ; add, addx. 1350 | lea Addmsg(pc),a1 1351 | 1352 | Handle_Add_AddX_Sub_SubX: 1353 | bsr.w Print_Message 1354 | ; Is this addx/subx? 1355 | move.w d5,d1 1356 | andi.w #$130,d1 1357 | cmpi.w #$100,d1 1358 | beq.w Handle_AddX_SubX ; Branch if yes. 1359 | ; Is this add/sub? 1360 | move.w d5,d3 1361 | andi.w #$C0,d3 1362 | lsr.w #5,d3 1363 | cmpi.b #6,d3 1364 | bne.s Handle_Add_And_Or_Sub ; Branch if yes. 1365 | ; adda/suba. 1366 | vtputc "a",BLUE 1367 | moveq #2,d3 1368 | btst #8,d5 1369 | beq.s .got_size 1370 | add.b d3,d3 1371 | 1372 | .got_size: 1373 | lea SizeMsgs(pc),a1 1374 | adda.w (a1,d3.w),a1 1375 | bsr.w Print_Message 1376 | lsr.w #1,d3 1377 | move.w d5,d1 ; Effective address bits 1378 | moveq #0,d4 ; No additional immediate parameters 1379 | jsr (a4) ; Rewind a3 to instruction+2 if needed 1380 | lea (a3),a2 1381 | bsr.w Handle_effective_address 1382 | vtputc "," 1383 | move.w d5,d1 1384 | andi.w #$E00,d1 1385 | rol.w #7,d1 1386 | bra.w Reg_Addr_Direct 1387 | ; =========================================================================== 1388 | Handle_Add_And_Or_Sub: 1389 | ; add, and, or, sub. 1390 | lea SizeMsgs(pc),a1 1391 | adda.w (a1,d3.w),a1 1392 | bsr.w Print_Message 1393 | lsr.w #1,d3 1394 | move.w d5,d1 ; Effective address bits 1395 | moveq #0,d4 ; No additional immediate parameters 1396 | jsr (a4) ; Rewind a3 to instruction+2 if needed 1397 | lea (a3),a2 1398 | btst #8,d5 1399 | bne.s .ea_second 1400 | ; First parameter is effective address. 1401 | bsr.w Handle_effective_address 1402 | vtputc "," 1403 | 1404 | .ea_second: 1405 | ; Print register. 1406 | move.w d5,d1 1407 | andi.w #$E00,d1 1408 | rol.w #7,d1 1409 | btst #8,d5 1410 | beq.w Reg_Data_Direct 1411 | bsr.w Reg_Data_Direct 1412 | ; Second parameter is effective address. 1413 | vtputc "," 1414 | bra.w Handle_effective_address 1415 | ; =========================================================================== 1416 | Handle_AddX_SubX: 1417 | ; addx/subx. 1418 | vtputc "x",BLUE 1419 | move.w d5,d3 1420 | andi.w #$C0,d3 1421 | lsr.w #5,d3 1422 | lea SizeMsgs(pc),a1 1423 | adda.w (a1,d3.w),a1 1424 | bsr.w Print_Message 1425 | 1426 | Handle_AddX_Abcd_SubX_Sbcd_Common: 1427 | ; Common to lots of instructions. 1428 | lea Reg_Data_Direct(pc),a4 1429 | ; Bit 3 determines if parameters are Dn or -(An). 1430 | btst #3,d5 1431 | beq.s .got_type 1432 | lea Reg_Addr_Pre_Indirect(pc),a4 1433 | 1434 | .got_type: 1435 | move.w d5,d1 1436 | andi.w #7,d1 1437 | jsr (a4) 1438 | vtputc "," 1439 | move.w d5,d1 1440 | andi.w #$E00,d1 1441 | rol.w #7,d1 1442 | lea (a3),a2 1443 | jmp (a4) 1444 | ; =========================================================================== 1445 | Addmsg: vtstring BLUE,"add" 1446 | Submsg: vtstring BLUE,"sub" 1447 | ; =========================================================================== 1448 | Dis_Shift_Rotate: ; %1110 1449 | ; Shift and rotate instructions. 1450 | ; Filter out invalid instructions on the MC68000. 1451 | move.w d5,d1 1452 | andi.w #$8C0,d1 1453 | cmpi.w #$8C0,d1 1454 | beq.w IllegalInstruction 1455 | 1456 | andi.w #$C0,d1 1457 | move.w d5,d2 1458 | cmpi.w #$C0,d1 ; Are we doing a register or memory shift? 1459 | beq.s .mem_shift ; Branch if memory shift 1460 | ; Register shift 1461 | andi.w #$18,d2 1462 | lsr.w #2,d2 1463 | lsr.w #5,d1 1464 | bra.s .get_msg 1465 | ; =========================================================================== 1466 | .mem_shift: 1467 | ; Memory shift 1468 | andi.w #$600,d2 1469 | lsr.w #8,d2 1470 | moveq #-1,d1 1471 | 1472 | .get_msg: 1473 | lea ShiftMessages(pc),a1 1474 | adda.w (a1,d2.w),a1 1475 | bsr.w Print_Message 1476 | ; Bit 8 indicates direction of shift. 1477 | btst #8,d5 1478 | beq.s .shiftr 1479 | vtputc "l",BLUE 1480 | bra.s .got_dir 1481 | ; --------------------------------------------------------------------------- 1482 | .shiftr: 1483 | vtputc "r",BLUE 1484 | 1485 | .got_dir: 1486 | lea WordSize(pc),a1 1487 | tst.b d1 1488 | bmi.s .got_size 1489 | lea SizeMsgs(pc),a1 1490 | adda.w (a1,d1.w),a1 1491 | 1492 | .got_size: 1493 | bsr.w Print_Message 1494 | 1495 | tst.b d1 1496 | bmi.s .MemoryShift 1497 | 1498 | move.w d5,d2 1499 | andi.w #$E00,d2 1500 | rol.w #7,d2 1501 | ; Register shift 1502 | btst #5,d5 1503 | bne.s .reg_cnt 1504 | ; Static count 1505 | vtputc "#" 1506 | tst.b d2 1507 | bne.s .got_cnts 1508 | addq.w #8,d2 1509 | 1510 | .got_cnts: 1511 | itoa.w d2,RED 1512 | bra.s .RegShift_Common 1513 | 1514 | ; --------------------------------------------------------------------------- 1515 | .reg_cnt: 1516 | vtputc "d",GREEN 1517 | itoa.w d2,GREEN 1518 | 1519 | .RegShift_Common: 1520 | vtput.w d2 1521 | vtputc "," 1522 | move.w d5,d1 1523 | andi.w #7,d1 1524 | lea (a3),a2 1525 | bra.w Reg_Data_Direct 1526 | ; =========================================================================== 1527 | .MemoryShift: 1528 | moveq #1,d3 ; Word operation 1529 | move.w d5,d1 ; Effective address bits 1530 | moveq #0,d4 ; No additional immediate parameters 1531 | jsr (a4) ; Rewind a3 to instruction+2 if needed 1532 | lea (a3),a2 1533 | bra.w Handle_effective_address 1534 | ; =========================================================================== 1535 | ShiftMessages: offsetTable 1536 | offsetTableEntry.w ASdmsg 1537 | offsetTableEntry.w LSdmsg 1538 | offsetTableEntry.w ROXdmsg 1539 | offsetTableEntry.w ROdmsg 1540 | ASdmsg: vtstring BLUE,"as" 1541 | LSdmsg: vtstring BLUE,"ls" 1542 | ROXdmsg: vtstring BLUE,"rox" 1543 | ROdmsg: vtstring BLUE,"ro" 1544 | ; =========================================================================== 1545 | Line1010Emulator: ; %1010 1546 | Line1111Emulator: ; %1111 1547 | ; Neither of these can cause address errors, so don't bother with the rewinding 1548 | ; portion. In systems where bus errors are possible, this could be relevant. 1549 | lea (a3),a2 1550 | lea DeclareWord(pc),a1 1551 | bsr.w Print_Message 1552 | move.w d5,d0 1553 | bra.w Print_Word 1554 | ; =========================================================================== 1555 | IllegalInstruction: 1556 | ; This can't cause address errors, so don't bother with the rewinding portion. 1557 | ; In systems where bus errors are possible, this could be relevant. 1558 | lea (a3),a2 1559 | lea DeclareWord(pc),a1 1560 | bsr.w Print_Message 1561 | move.w d5,d0 1562 | bsr.w Print_Word 1563 | lea IllegalInstructionMsg(pc),a1 1564 | bra.w Print_Message 1565 | ; =========================================================================== 1566 | IllegalInstructionMsg: vtstring RED ," ; Illegal instruction!" 1567 | DeclareWord: vtstring BLUE,"dc.w " 1568 | 1569 | SizeMsgs: offsetTable 1570 | offsetTableEntry.w ByteSize 1571 | offsetTableEntry.w WordSize 1572 | offsetTableEntry.w LongSize 1573 | offsetTableEntry.w NoSize 1574 | ShortSize: vtstring BLUE,".s " 1575 | ByteSize: vtstring BLUE,".b " 1576 | WordSize: vtstring BLUE,".w " 1577 | LongSize: vtstring BLUE,".l " 1578 | NoSize: vtstring BLUE," " 1579 | ; =========================================================================== 1580 | ; Prints effective address for opcode 1581 | ; Input: 1582 | ; a2 Pointer to address just before extension word (if any) 1583 | ; a5 Write destination (in RAM) 1584 | ; d3 Instruction size (%00 = byte, %01 = word, %10 = long, all others = invalid) 1585 | ; d5 Instruction register 1586 | ; Output: 1587 | ; a2 Pointer to address just after extension word (if any) 1588 | Handle_effective_address: 1589 | move.w d5,d0 1590 | move.w d5,d1 1591 | 1592 | Handle_effective_address_Part2: 1593 | andi.w #$38,d0 1594 | lsr.w #2,d0 1595 | andi.w #$7,d1 1596 | move.w EACodeMap(pc,d0.w),d0 1597 | jmp EACodeMap(pc,d0.w) 1598 | ; =========================================================================== 1599 | EACodeMap: offsetTable 1600 | offsetTableEntry.w Reg_Data_Direct ; %000 1601 | offsetTableEntry.w Reg_Addr_Direct ; %001 1602 | offsetTableEntry.w Reg_Addr_Indirect ; %010 1603 | offsetTableEntry.w Reg_Addr_Post_Indirect ; %011 1604 | offsetTableEntry.w Reg_Addr_Pre_Indirect ; %100 1605 | offsetTableEntry.w Reg_Addr_Disp_Indirect ; %101 1606 | offsetTableEntry.w Reg_Addr_Index_Indirect ; %110 1607 | offsetTableEntry.w Misc_EA_Modes ; %111 1608 | ; =========================================================================== 1609 | Reg_Data_Direct: 1610 | ;Dn 1611 | vtputc "d",GREEN 1612 | itoa.w d1,GREEN 1613 | vtput.w d1 1614 | rts 1615 | ; =========================================================================== 1616 | Reg_Addr_Direct: 1617 | cmpi.b #7,d1 1618 | beq.s .print_sp 1619 | ;An 1620 | vtputc "a",GREEN 1621 | itoa.w d1,GREEN 1622 | vtput.w d1 1623 | rts 1624 | ; --------------------------------------------------------------------------- 1625 | .print_sp: 1626 | vtputs "sp",GREEN 1627 | rts 1628 | ; =========================================================================== 1629 | Reg_Addr_Indirect: 1630 | ;(An) 1631 | vtputc "(" 1632 | bsr.s Reg_Addr_Direct 1633 | vtputc ")" 1634 | rts 1635 | ; =========================================================================== 1636 | Reg_Addr_Post_Indirect: 1637 | ;(An)+ 1638 | bsr.s Reg_Addr_Indirect 1639 | vtputc "+" 1640 | rts 1641 | ; =========================================================================== 1642 | Reg_Addr_Pre_Indirect: 1643 | ;-(An) 1644 | vtputc "-" 1645 | bra.s Reg_Addr_Indirect 1646 | ; =========================================================================== 1647 | Reg_Addr_Disp_Indirect: 1648 | ;d16(An) 1649 | move.w (a2)+,d0 1650 | beq.s Reg_Addr_Indirect 1651 | bsr.w Print_Word_Signed 1652 | bra.s Reg_Addr_Indirect 1653 | ; =========================================================================== 1654 | Reg_Addr_Index_Indirect: 1655 | ;d8(An,Xn.S) 1656 | move.w (a2)+,d0 1657 | tst.b d0 1658 | beq.s .skip_null 1659 | bsr.w Print_Byte_Signed 1660 | 1661 | .skip_null: 1662 | vtputc "(" 1663 | bsr.s Reg_Addr_Direct 1664 | vtputc "," 1665 | 1666 | Common_Index_Indirect: 1667 | move.w d0,d1 1668 | andi.w #$7000,d1 1669 | rol.w #4,d1 1670 | lea Reg_Data_Direct(pc),a0 1671 | btst #15,d0 1672 | beq.s .not_addr 1673 | lea Reg_Addr_Direct(pc),a0 1674 | 1675 | .not_addr: 1676 | jsr (a0) 1677 | vtputc "." 1678 | btst #11,d0 1679 | beq.s .size_w 1680 | vtputc "l",GREEN 1681 | bra.s .got_size 1682 | ; --------------------------------------------------------------------------- 1683 | .size_w: 1684 | vtputc "w",GREEN 1685 | 1686 | .got_size: 1687 | vtputc ")" 1688 | rts 1689 | ; =========================================================================== 1690 | Misc_EA_Modes: 1691 | lsl.w #1,d1 1692 | move.w MiscEACodeMap(pc,d1.w),d1 1693 | jmp MiscEACodeMap(pc,d1.w) 1694 | ; =========================================================================== 1695 | MiscEACodeMap: offsetTable 1696 | offsetTableEntry.w Abs_Short ; %000 1697 | offsetTableEntry.w Abs_Long ; %001 1698 | offsetTableEntry.w PC_Disp_Indirect ; %010 1699 | offsetTableEntry.w PC_Index_Indirect ; %011 1700 | offsetTableEntry.w Immediate ; %100 1701 | offsetTableEntry.w InvalidAddrMode ; %101 1702 | offsetTableEntry.w InvalidAddrMode ; %110 1703 | offsetTableEntry.w InvalidAddrMode ; %111 1704 | ; =========================================================================== 1705 | Abs_Short: 1706 | vtputc "(" 1707 | move.w (a2)+,d0 1708 | ext.l d0 1709 | bsr.w Print_Address 1710 | vtputs ").w",BLUE 1711 | rts 1712 | ; =========================================================================== 1713 | Abs_Long: 1714 | vtputc "(" 1715 | move.l (a2)+,d0 1716 | bsr.w Print_Address 1717 | vtputs ").l",BLUE 1718 | rts 1719 | ; =========================================================================== 1720 | PC_Disp_Indirect: 1721 | ;d16(PC) 1722 | move.w (a2)+,d0 1723 | ext.l d0 1724 | add.l a2,d0 1725 | subq.l #2,d0 1726 | bsr.w Print_Address 1727 | vtputs "(pc)",GREEN 1728 | rts 1729 | ; =========================================================================== 1730 | PC_Index_Indirect: 1731 | ;d8(PC,Xn.S) 1732 | move.w (a2),d0 1733 | ext.w d0 1734 | ext.l d0 1735 | add.l a2,d0 1736 | bsr.w Print_Address 1737 | move.w (a2)+,d0 1738 | vtputs "(pc,",GREEN 1739 | bra.w Common_Index_Indirect 1740 | ; =========================================================================== 1741 | Immediate: 1742 | ;#value 1743 | vtputc "#" 1744 | cmpi.b #2,d3 1745 | bhs.s LongImmed 1746 | move.w (a2)+,d0 1747 | tst.b d3 1748 | beq.w Print_Byte 1749 | bra.w Print_Word 1750 | ; =========================================================================== 1751 | LongImmed: 1752 | move.l (a2)+,d0 1753 | bra.w Print_Long 1754 | ; =========================================================================== 1755 | InvalidAddrMode: ; %101,%110,%111 1756 | lea InvalidAddrModeMsg(pc),a1 1757 | bra.w Print_Message 1758 | ; =========================================================================== 1759 | InvalidAddrModeMsg: vtstring BLUE,"\x7FInvalid address mode\x7F" 1760 | ; =========================================================================== 1761 | 1762 | -------------------------------------------------------------------------------- /src/Terminal.asm: -------------------------------------------------------------------------------- 1 | ; =========================================================================== 2 | ; Copyright (C) 2011-2018 by flamewing 3 | ; 4 | ; Permission to use, copy, modify, and/or distribute this software for any 5 | ; purpose with or without fee is hereby granted. 6 | ; 7 | ; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | ; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | ; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | ; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 13 | ; OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | ; =========================================================================== 15 | include "vdp-macros.asm" 16 | ; Make sure these point to the correct locations. 17 | ; --------------------------------------------------------------------------- 18 | ; Kosinski compressed art (60 tiles) 19 | ; Terminal font; contains all ASCII characters. For the full list of characters, 20 | ; see https://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters 21 | ArtKos_TerminalFont: BINCLUDE "TerminalFont.bin" 22 | even 23 | ; --------------------------------------------------------------------------- 24 | ; Palette 25 | ; The terminal palette 26 | TerminalPalette: BINCLUDE "TerminalPal.bin" 27 | TerminalPalette_Len = *-TerminalPalette 28 | even 29 | ; =========================================================================== 30 | ; Location of art in VRAM. Just use these defaults. 31 | ArtTile_ArtKos_TerminalFont = $0021 32 | TerminalFont_Len = tiles_to_bytes($60) 33 | ; =========================================================================== 34 | ; Location in RAM. Just use this default. 35 | TerminalBuffer = ramaddr($FFFF0000) 36 | ; =========================================================================== 37 | ; Locations in VRAM. Just use this default. 38 | VRAM_Terminal_Window_Name_Table = $F000 ; Extends until $FFFF 39 | VRAM_Terminal_Plane_A_Name_Table = $C000 ; Extends until $CFFF 40 | VRAM_Terminal_Plane_B_Name_Table = $E000 ; Extends until $EFFF 41 | VRAM_Terminal_Plane_Table_Size = planeSizeBytes(64,32) ; 64 cells x 32 cells x 2 bytes per cell 42 | VRAM_Terminal_Sprite_Attribute_Table = $F800 ; Extends until $FA7F 43 | VRAM_Terminal_Sprite_Attribute_Table_Size = $0280 ; 640 bytes 44 | VRAM_Terminal_Horiz_Scroll_Table = $DC00 ; Extends until $DF7F 45 | VRAM_Terminal_Horiz_Scroll_Table_Size = $0380 ; 224 lines * 2 bytes per entry * 2 PNTs 46 | ; =========================================================================== 47 | ; NO USER SERVICEABLE PARTS BELOW THIS POINT 48 | ; =========================================================================== 49 | WHITE = palette_line_0 50 | BLUE = palette_line_1 51 | RED = palette_line_2 52 | GREEN = palette_line_3 53 | ; =========================================================================== 54 | ; Function to aid in moving the cursor to a given location. 55 | nCols = 40 56 | nRows = 28 57 | DisplayCell function col,line,((2 * nCols * line) + (2 * col)) 58 | ; =========================================================================== 59 | ; Macro for defining strings suitable for efficient use with the functions in 60 | ; this module. Adds a length prefix and maps characters to have the correct 61 | ; colors, as needed. 62 | vtstring macro pal,letters 63 | dc.w strlen(letters) 64 | irpc char,letters 65 | ; Note: ? is not on the list 66 | if strstr("*@[\\]()+,-./:;#$%&! ","char")>=0 67 | dc.w "char" ; raw character (no palette modifier) 68 | else 69 | dc.b pal>>8,"char" ; output letter code 70 | endif 71 | endm 72 | endm 73 | ; =========================================================================== 74 | ; Macro that puts a character in the buffer with the given color, if any. 75 | ; In general, punctuation characters are uncolored by default. 76 | vtputc macro char,clr 77 | set .r,charfromstr(char,0) 78 | ; Note: ? is not on the list 79 | if strstr("*@[\\]()+,-./:;#$%&! ",char)>=0 80 | move.w #.r,(a5)+ ; raw character (no palette modifier) 81 | elseif "clr"<>"" 82 | move.w #clr|.r,(a5)+ ; output letter code 83 | else 84 | move.w #.r,(a5)+ ; output letter code 85 | endif 86 | endm 87 | ; =========================================================================== 88 | ; Macro that puts a string of characters in the buffer with the given color, if 89 | ; any. Uses vtputc internally. 90 | vtputs macro letters,clr 91 | irpc char,letters 92 | if "char\t"=="\\t" 93 | vtputc "\\",clr 94 | else 95 | vtputc "char",clr 96 | endif 97 | endm 98 | endm 99 | ; =========================================================================== 100 | ; Macro that puts the character or pair of characters at the given effective 101 | ; address using the given color, if any. 102 | vtput macro reg,clr 103 | if "ATTRIBUTE"=="b" 104 | andi.w #$FF,reg 105 | endif 106 | if "ATTRIBUTE"<>"l" 107 | if "clr"<>"" 108 | addi.w #clr,reg 109 | endif 110 | move.w reg,(a5)+ ; output letter code 111 | else 112 | if "clr"<>"" 113 | addi.l #(clr<<16)|clr,reg 114 | endif 115 | move.l reg,(a5)+ ; output letter codes 116 | endif 117 | endm 118 | ; =========================================================================== 119 | ; Converts the given effective address from numbers to ASCII numbers, using the 120 | ; given color if any. 121 | itoa macro reg,clr 122 | if "ATTRIBUTE"=="b" 123 | andi.w #$FF,reg 124 | endif 125 | if "ATTRIBUTE"<>"l" 126 | if "clr"<>"" 127 | addi.w #'0'|clr,reg 128 | else 129 | addi.w #'0',reg 130 | endif 131 | else 132 | if "clr"<>"" 133 | addi.l #(('0'|clr)<<16)|('0'|clr),reg 134 | else 135 | addi.l #('0'<<16)|'0',reg 136 | endif 137 | endif 138 | endm 139 | ; =========================================================================== 140 | ; Gets current position of the put pointer. 141 | tellp macro dst 142 | move.ATTRIBUTE a5,dst 143 | endm 144 | ; =========================================================================== 145 | ; Moves the put pointer by the given distance. 146 | seekp macro off,dst 147 | if "dst"<>"" 148 | add.ATTRIBUTE #DisplayCell(off,0),dst 149 | else 150 | adda.ATTRIBUTE #DisplayCell(off,0),a5 151 | endif 152 | endm 153 | ; =========================================================================== 154 | ; Sets the put pointer based on the given effective address. 155 | seekset macro src 156 | movea.l src,a5 157 | endm 158 | ; =========================================================================== 159 | ; Sets the put pointer to the given column and row. 160 | setcursor macro col,row,dst 161 | if "dst"<>"" 162 | lea (TerminalBuffer + DisplayCell(col,row)).l,dst 163 | else 164 | lea (TerminalBuffer + DisplayCell(col,row)).l,a5 165 | endif 166 | endm 167 | ; =========================================================================== 168 | ; Gets the distance, in characters, between the put pointer and the value given 169 | ; in the effective address. 170 | getdst macro dst 171 | sub.ATTRIBUTE a5,dst 172 | asr.w #1,dst 173 | endm 174 | ; =========================================================================== 175 | ; As getdst, but gives the return in bytes instead. 176 | vtchkp macro dst 177 | sub.ATTRIBUTE a5,dst 178 | endm 179 | ; =========================================================================== 180 | ; Puts cn copies of the given character, in sequence, on the terminal. 181 | fillr macro src,cnt 182 | .fill_loop: 183 | vtput.w src 184 | dbra cnt,.fill_loop 185 | endm 186 | ; =========================================================================== 187 | ; Clears the entire terminal. 188 | vtcls macro 189 | move.w #bytesToLcnt(2 * nRows * nCols),d0 190 | moveq #0,d1 191 | setcursor 0,0 192 | .clr_screen: 193 | vtput.l d1 194 | dbra d0,.clr_screen 195 | endm 196 | ; =========================================================================== 197 | ; Initialize Terminal 198 | InitTerminal: 199 | stopZ80 200 | 201 | lea (VDP_control_port).l,a3 202 | tst.w (a3) ; Reset "write-pending" flag 203 | move.w #regMode1(HINT_OFF|HVLATCH_OFF),(a3) ; H-INT disabled, Enable HV counter read 204 | move.w #regMode2(DISPLAY_OFF|VINT_OFF|DMA_ON|V30_OFF|MODE_GEN),(a3) ; Display disabled, Genesis mode, DMA enabled, V-INT disabled, V res 28 cells. 205 | move.w #regPlaneA(VRAM_Terminal_Plane_A_Name_Table),(a3) ; PNT A base: $C000 206 | move.w #regWindow(VRAM_Terminal_Window_Name_Table),(a3) ; Window Address: $F000 207 | move.w #regPlaneB(VRAM_Terminal_Plane_B_Name_Table),(a3) ; PNT B base: $E000 208 | move.w #regSprite(VRAM_Terminal_Sprite_Attribute_Table),(a3) ; Sprite attribute table base: $F800 209 | move.w #regBaseSprite(SPRITES_LOW),(a3) ; Sprite Pattern Generator Base Address: low 64KB VRAM 210 | move.w #regBGColor(0,0),(a3) ; Background palette/color: 0/0 211 | move.w #regMode4HScroll(0),(a3) ; Mode 4 H-Scroll: 0 212 | move.w #regMode4VScroll(0),(a3) ; Mode 4 V-Scroll: 0 213 | move.w #regHIntLine($FF),(a3) ; H-INT every $FF scanlines 214 | move.w #regMode3(EXINT_OFF|VSCROLL_FULL|HSCROLL_FULL),(a3) ; EXT-INT off, V scroll by screen, H scroll by screen 215 | move.w #regMode4(MODE_H40|SHADOWHILITE_OFF|INTERLACE_OFF),(a3) ; H res 40 cells, no interlace, S/H disabled 216 | move.w #regScroll(VRAM_Terminal_Horiz_Scroll_Table),(a3) ; HScroll Table Address: $DC00 217 | move.w #regBasePlane(PLANE_B_LOW|PLANE_A_LOW),(a3) ; Nametable Pattern Generator Base Address on low 64KB VRAM 218 | move.w #regAutoIncr(2),(a3) ; VRAM pointer increment: $0002 219 | move.w #regPlaneSize(64,32),(a3) ; Scroll table size: 64x32 220 | move.w #regWindowHLoc(DOCK_LEFT,0),(a3) ; Window H left side, Base Point 0 221 | move.w #regWindowVLoc(DOCK_TOP,0),(a3) ; Window V upside, Base Point 0 222 | 223 | ; Copy the palette to CRAM 224 | dma68kToVDP TerminalPalette,$0000,TerminalPalette_Len,CRAM 225 | 226 | ; Clear vertical scroll 227 | moveq #0,d0 228 | lea (VDP_data_port).l,a1 229 | move.l #vdpComm($0000,VSRAM,WRITE),(a3) 230 | move.l d0,(a1) 231 | 232 | ; Fill VRAM with 0 233 | dmaFillVRAM 0,$0000,$10000 234 | 235 | ; Decode and load graphics. 236 | lea ArtKos_TerminalFont(pc),a0 237 | lea (Chunk_Table).l,a1 238 | ifdef debugger_blob 239 | jsr KosDec(pc) 240 | else 241 | jsr (KosDec).w 242 | endif 243 | dma68kToVDP TerminalBuffer,tiles_to_bytes(ArtTile_ArtKos_TerminalFont),TerminalFont_Len,VRAM 244 | 245 | vtcls ; Clear screen 246 | rts 247 | ; =========================================================================== 248 | planeLocH40_ function col,line,(($80 * line) + (2 * col)) 249 | ; --------------------------------------------------------------------------- 250 | DrawTerminal: 251 | ; Inlined the call to PlaneMapToVRAM_H40 for one less external dependency. 252 | lea (TerminalBuffer).l,a1 253 | lea (VDP_data_port).l,a3 254 | lea (VDP_control_port).l,a2 255 | move.l #vdpCommDelta(planeLocH40_(0,1)),d4 256 | move.l #vdpComm(VRAM_Terminal_Plane_A_Name_Table,VRAM,WRITE),d0 257 | moveq #nCols-1,d1 ; Width 258 | moveq #nRows-1,d2 ; Height 259 | 260 | .row_loop: 261 | move.l d0,(a2) ; Set write destination 262 | move.w d1,d3 263 | 264 | .col_loop: 265 | move.w (a1)+,(a3) 266 | dbra d3,.col_loop 267 | add.l d4,d0 ; increase destination address by 1 line 268 | dbra d2,.row_loop 269 | 270 | ; Exit blanking mode. 271 | move.w #regMode2(DISPLAY_ON|VINT_OFF|DMA_ON|V30_OFF|MODE_GEN),(a2) ; Display enabled, Genesis mode, DMA enabled, V-INT disabled, V res 28 cells. 272 | rts 273 | ; =========================================================================== 274 | ; SUBROUTINE 275 | ; Prints a message to the screen. 276 | ; Input: 277 | ; a1 Message to print 278 | ; a5 Write destination (in RAM) 279 | Print_Message: 280 | move.w d1,-(sp) 281 | move.w (a1)+,d1 ; Get size of string 282 | beq.s .done ; Branch out for empty strings 283 | subq.w #1,d1 ; Make it into a loop counter 284 | fillr (a1)+,d1 285 | 286 | .done: 287 | move.w (sp)+,d1 288 | rts 289 | ; =========================================================================== 290 | ; Macro to dump a hex number. Prints the nibbles stored in d0 according to the 291 | ; specified attribute. 292 | HexDump macro 293 | ; Select number of nibbles to print based on attribute given. 294 | switch "ATTRIBUTE" 295 | case "b" 296 | moveq #1,d2 297 | case "w" 298 | moveq #3,d2 299 | case "l" 300 | moveq #7,d2 301 | endcase 302 | move.w d1,-(sp) 303 | 304 | .put_digit_loop: 305 | rol.ATTRIBUTE #4,d0 ; Get a new nibble to print 306 | move.b d0,d1 ; Move to d1 so the other nibbles are kept 307 | andi.w #$F,d1 ; Want only the lowest nibble 308 | move.b Hex2Char(pc,d1.w),d1 ; Convert to character 309 | addi.w #RED,d1 ; Set number color 310 | vtput.w d1 311 | dbra d2,.put_digit_loop 312 | move.w (sp)+,d1 313 | endm 314 | ; =========================================================================== 315 | ; Macro to define functions that print hex numbers. 316 | HexPrint macro {INTLABEL} 317 | Print___LABEL___Signed label * 318 | tst.ATTRIBUTE d0 319 | bpl.s Print___LABEL__ 320 | vtputc "-" 321 | neg.ATTRIBUTE d0 322 | 323 | Print___LABEL__ label * 324 | vtputc "$" 325 | 326 | HexDump___LABEL__ label * 327 | HexDump.ATTRIBUTE 328 | rts 329 | endm 330 | ; =========================================================================== 331 | ; SUBROUTINE 332 | ; Prints a byte to the screen. Defines the functions Print_Byte_Signed, 333 | ; Print_Byte and HexDump_Byte. 334 | ; Input: 335 | ; d0 Byte to print 336 | ; a5 Write destination (in RAM) 337 | Byte: HexPrint.b 338 | ; =========================================================================== 339 | ; SUBROUTINE 340 | ; Prints a word to the screen. Defines the functions Print_Word_Signed, 341 | ; Print_Word and HexDump_Word. 342 | ; Input: 343 | ; d0 Word to print 344 | ; a5 Write destination (in RAM) 345 | Word: HexPrint.w 346 | ; =========================================================================== 347 | Hex2Char: 348 | dc.b '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' 349 | ; =========================================================================== 350 | ; SUBROUTINE 351 | ; Prints a 24-bit address to the screen, including dollar sign. 352 | ; Input: 353 | ; d0 Address to print 354 | ; a5 Write destination (in RAM) 355 | Print_Address: 356 | vtputc "$" 357 | swap d0 358 | HexDump.b 359 | swap d0 360 | HexDump.w 361 | rts 362 | ; =========================================================================== 363 | ; SUBROUTINE 364 | ; Prints a long to the screen. Defines the functions Print_Long_Signed, 365 | ; Print_Long and HexDump_Long. 366 | ; Input: 367 | ; d0 Long to print 368 | ; a5 Write destination (in RAM) 369 | Long: HexPrint.l 370 | ; =========================================================================== 371 | 372 | -------------------------------------------------------------------------------- /src/TerminalFont.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flamewing/genesis-debugger/20fd1d8e21379b0a733a6b6ecc9a4fa8a3622d00/src/TerminalFont.bin -------------------------------------------------------------------------------- /src/TerminalPal.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flamewing/genesis-debugger/20fd1d8e21379b0a733a6b6ecc9a4fa8a3622d00/src/TerminalPal.bin -------------------------------------------------------------------------------- /src/skcompat.asm: -------------------------------------------------------------------------------- 1 | ; =========================================================================== 2 | ; Copyright (C) 2011-2018 by flamewing 3 | ; 4 | ; Permission to use, copy, modify, and/or distribute this software for any 5 | ; purpose with or without fee is hereby granted. 6 | ; 7 | ; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | ; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | ; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | ; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 13 | ; OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | ; =========================================================================== 15 | ; A few constants 16 | palette_line_0 = (0<<13) 17 | palette_line_1 = (1<<13) 18 | palette_line_2 = (2<<13) 19 | palette_line_3 = (3<<13) 20 | tile_mask = $07FF 21 | 22 | ; Remapped function names 23 | KosDec EQU Kos_Decomp 24 | EniDec EQU Eni_Decomp 25 | 26 | ; Remapped RAM locations 27 | Chunk_Table EQU Chunk_table 28 | System_Stack EQU System_stack 29 | 30 | ; VRAM constants 31 | ArtTile_VRAM_Start = $0000 32 | 33 | ; simplifying macros and functions 34 | 35 | ; makes a VDP address difference 36 | vdpCommDelta function addr,((addr&$3FFF)<<16)|((addr&$C000)>>14) 37 | 38 | ; macros to convert from tile index to art tiles, block mapping or VRAM address. 39 | make_art_tile function addr,pal,pri,((pri&1)<<15)|((pal&3)<<13)|(addr&tile_mask) 40 | tiles_to_bytes function addr,((addr&$7FF)<<5) 41 | 42 | ; macro to declare an offset table 43 | offsetTable macro {INTLABEL} 44 | current_offset_table := __LABEL__ 45 | __LABEL__ label * 46 | endm 47 | 48 | ; macro to declare an entry in an offset table 49 | offsetTableEntry macro ptr 50 | dc.ATTRIBUTE ptr-current_offset_table 51 | endm 52 | 53 | -------------------------------------------------------------------------------- /src/vdp-macros.asm: -------------------------------------------------------------------------------- 1 | ; =========================================================================== 2 | ; Copyright (C) 2011-2020 by flamewing 3 | ; 4 | ; Permission to use, copy, modify, and/or distribute this software for any 5 | ; purpose with or without fee is hereby granted. 6 | ; 7 | ; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | ; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | ; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | ; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 13 | ; OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | ; =========================================================================== 15 | ifndef VDP_Macros 16 | ; Note: if you are adding those to S2 and S3&K disassemblies, make sure to 17 | ; also add this constant definition. 18 | VDP_Macros := 1 19 | ; For register $80 - Mode Register 1 20 | COL0_BLANK_OFF = %00000000 ; $00 21 | COL0_BLANK_ON = %00100000 ; $20 22 | HINT_OFF = %00000000 ; $00 23 | HINT_ON = %00010000 ; $10 24 | PALSEL_OFF = %00000000 ; $00 25 | PALSEL_ON = %00000100 ; $04 26 | HVLATCH_OFF = %00000000 ; $00 27 | HVLATCH_ON = %00000010 ; $02 28 | ECSYNC_OFF = %00000000 ; $00 29 | ECSYNC_ON = %00000001 ; $01 30 | MODE1_MASK = COL0_BLANK_ON|HINT_ON|PALSEL_ON|HVLATCH_ON|ECSYNC_ON 31 | 32 | ; For register $81 - Mode Register 2 33 | VRAM_128KB_OFF = %00000000 ; $00 34 | VRAM_128KB_ON = %10000000 ; $80 35 | DISPLAY_OFF = %00000000 ; $00 36 | DISPLAY_ON = %01000000 ; $40 37 | VINT_OFF = %00000000 ; $00 38 | VINT_ON = %00100000 ; $20 39 | DMA_OFF = %00000000 ; $00 40 | DMA_ON = %00010000 ; $10 41 | V30_OFF = %00000000 ; $00 42 | V30_ON = %00001000 ; $08 43 | MODE_SMS = %00000000 ; $00 44 | MODE_GEN = %00000100 ; $04 45 | MODE2_MASK = VRAM_128KB_ON|DISPLAY_ON|VINT_ON|DMA_ON|V30_ON|MODE_GEN 46 | 47 | ; For register $86 - Sprite Pattern Generator Base Address 48 | SPRITES_LOW = %00000000 ; $00 49 | SPRITES_HIGH = %00100000 ; $20 50 | 51 | ; For register $8B - Mode Register 3 52 | EXINT_OFF = %00000000 ; $00 53 | EXINT_ON = %00001000 ; $08 54 | VSCROLL_FULL = %00000000 ; $00 55 | VSCROLL_CELL = %00000100 ; $04 56 | HSCROLL_FULL = %00000000 ; $00 57 | HSCROLL_TILE = %00000010 ; $02 58 | HSCROLL_LINE = %00000011 ; $03 59 | MODE3_MASK = EXINT_ON|VSCROLL_CELL|HSCROLL_LINE 60 | 61 | ; For register $8C - Mode Register 4 62 | MODE_H32 = %00000000 ; $00 63 | MODE_H32_FAST = %10000000 ; $80 ; Out of spec for most TVs 64 | MODE_H40_FAST = %00000001 ; $01 ; OK for most TVs 65 | MODE_H40 = %10000001 ; $81 66 | VSYNC_NORMAL = %00000000 ; $00 67 | PIXEL_CLOCK = %01000000 ; $40 68 | HSYNC_NORMAL = %00000000 ; $00 69 | HSYNC_OFF = %00100000 ; $20 70 | PIXEL_BUS_OFF = %00000000 ; $00 71 | PIXEL_BUS_ON = %00010000 ; $10 72 | SHADOWHILITE_OFF = %00000000 ; $00 73 | SHADOWHILITE_ON = %00001000 ; $08 74 | INTERLACE_OFF = %00000000 ; $00 75 | INTERLACE_NORMAL = %00000010 ; $02 76 | INTERLACE_DOUBLE = %00000110 ; $06 77 | MODE4_MASK = MODE_H40|PIXEL_CLOCK|HSYNC_OFF|PIXEL_BUS_ON|SHADOWHILITE_ON|INTERLACE_DOUBLE 78 | 79 | ; For register $8E - Nametable Pattern Generator Base Address 80 | PLANE_B_LOW = %00000000 ; $00 81 | PLANE_B_HIGH = %00010000 ; $10 82 | PLANE_A_LOW = %00000000 ; $00 83 | PLANE_A_HIGH = %00000001 ; $00 84 | BASE_PLANE_MASK = PLANE_B_HIGH|PLANE_A_HIGH 85 | 86 | ; For register $91 - Window Plane Horizontal Position 87 | DOCK_LEFT = %00000000 ; $00 88 | DOCK_RIGHT = %10000000 ; $80 89 | 90 | ; For register $92 - Window Plane Vertical Position 91 | DOCK_TOP = %00000000 ; $00 92 | DOCK_BOTTOM = %10000000 ; $80 93 | 94 | WINDOW_CELL_MASK = %00011111 ; $1F 95 | 96 | ; For register $97 - DMA Source 97 | DMA_FLAG = %00000000 ; $00 98 | FILL_FLAG = %10000000 ; $80 99 | COPY_FLAG = %11000000 ; $C0 100 | 101 | valMode1 function mode,((mode&MODE1_MASK)|PALSEL_ON) 102 | valMode2 function mode,(mode&MODE2_MASK) 103 | addrPlaneA function addr,(addr/$400) 104 | addrWindow function addr,(addr/$400) 105 | addrPlaneB function addr,(addr/$2000) 106 | addrSprite function addr,(addr/$200) 107 | addrScroll function addr,(addr/$400) 108 | valBaseSprite function val,(val&SPRITES_HIGH) 109 | valBGColor function pal,index,((pal&3)<<4)|(index&$F) 110 | valMode3 function mode,(mode&MODE3_MASK) 111 | valMode4 function mode,(mode&MODE4_MASK) 112 | valBasePlane function val,(val&BASE_PLANE_MASK) 113 | valPlaneSize function width,height,(((height-32)/32)<<4)|((width-32)/32) 114 | valWindowLoc function dir,cells,(dir&$80)|(cells&WINDOW_CELL_MASK) 115 | valDMALenLow function length,length&$FF 116 | valDMALenHigh function length,(length&$FF00)>>8 117 | valDMASrcLow function length,length&$FF 118 | valDMASrcMid function length,(length&$FF00)>>8 119 | valDMASrcHigh function type,length,type|((length&$7F0000)>>16) 120 | 121 | regMode1 function mode,$8000|valMode1(mode) 122 | regMode2 function mode,$8100|valMode2(mode) 123 | regPlaneA function addr,$8200|addrPlaneA(addr) 124 | regWindow function addr,$8300|addrWindow(addr) 125 | regPlaneB function addr,$8400|addrPlaneB(addr) 126 | regSprite function addr,$8500|addrSprite(addr) 127 | regBaseSprite function val,$8600|valBaseSprite(val) 128 | regBGColor function pal,index,$8700|valBGColor(pal,index) 129 | regMode4HScroll function val,$8800|(val&$FF) 130 | regMode4VScroll function val,$8900|(val&$FF) 131 | regHIntLine function line,$8A00|(line&$FF) 132 | regMode3 function mode,$8B00|valMode3(mode) 133 | regMode4 function mode,$8C00|valMode4(mode) 134 | regScroll function addr,$8D00|addrScroll(addr) 135 | regBasePlane function val,$8E00|valBasePlane(val) 136 | regAutoIncr function delta,$8F00|(delta&$FF) 137 | regPlaneSize function width,height,$9000|valPlaneSize(width,height) 138 | regWindowHLoc function dir,cells,$9100|valWindowLoc(dir,cells) 139 | regWindowVLoc function dir,cells,$9200|valWindowLoc(dir,cells) 140 | regDMALenLow function length,$9300|valDMALenLow(length) 141 | regDMALenHigh function length,$9400|valDMALenHigh(length) 142 | regDMASrcLow function length,$9500|valDMASrcLow(length) 143 | regDMASrcMid function length,$9600|valDMASrcMid(length) 144 | regDMASrcHigh function type,length,$9700|valDMASrcHigh(type,length) 145 | 146 | planeSizeBytes function width,height,((height-32)/32)*((width-32)/32)*2 147 | 148 | ; Status register bits 149 | IS_PAL_BIT = 0 150 | DMA_ACTIVE_BIT = 1 151 | HBLANK_BIT = 2 152 | VBLANK_BIT = 3 153 | 154 | IS_PAL_MASK = 1<1 162 | if (length)==0 163 | fatal "DMA is copying 0 bytes (becomes a 64kB copy). If you really mean it, pass 64kB (65536) instead." 164 | endif 165 | endif 166 | lea (VDP_control_port).l,a5 167 | move.l #dmaCommLength(2*length),(a5) 168 | move.l #dmaCommSrcLow(src),(a5) 169 | move.l #makeLong(regAutoIncr(1),regDMASrcHigh(COPY_FLAG,0)),(a5) ; VRAM pointer increment: $0001, VRAM copy 170 | move.l #vdpComm(addr,VRAM,DMA),(a5) 171 | .loop: 172 | moveq #DMA_ACTIVE_MASK,d1 173 | and.w (a5),d1 174 | bne.s .loop ; busy loop until the VDP is finished filling... 175 | move.w #regAutoIncr(2),(a5) ; VRAM pointer increment: $0002 176 | endm 177 | endif 178 | --------------------------------------------------------------------------------