\ disasm.fs	disassembler file (for MIPS32)
\
\ Authors: Anton Ertl, David Kühling
\ Copyright (C) 2000,2007,2010,2014,2019 Free Software Foundation, Inc.

\ This file is part of Gforth.

\ Gforth is free software; you can redistribute it and/or
\ modify it under the terms of the GNU General Public License
\ as published by the Free Software Foundation, either version 3
\ of the License, or (at your option) any later version.

\ This program is distributed in the hope that it will be useful,
\ but WITHOUT ANY WARRANTY; without even the implied warranty of
\ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
\ GNU General Public License for more details.

\ You should have received a copy of the GNU General Public License
\ along with this program. If not, see http://www.gnu.org/licenses/.

\ this disassembler is based on data from the R4400 manual
\ http://www.mips.com/Documentation/R4400_Uman_book_Ed2.pdf, in
\ particular pages A3, A181, A182 (p. 471, 649, 650 in xpdf).
\ it is limited to the R3000 (MIPS-I) architecture, though.

\ test this with
\ gforth arch/mips/disasm.fs -e "here" arch/mips/testdisasm.fs -e "here over - disasm bye" |sed 's/([^)]*) //'|diff -u - arch/mips/testasm.fs

get-current
vocabulary disassembler
also disassembler definitions

\ instruction fields

: disasm-op ( w -- u )
    26 rshift ;

: disasm-rs ( w -- u )
    21 rshift $1F and ;

: disasm-rt ( w -- u )
    16 rshift $1f and ;

: disasm-rd ( w -- u )
    11 rshift $1f and ;

: disasm-shamt ( w -- u )
    \ shift amount field
    6 rshift $1f and ;

: disasm-funct ( w -- u )
    $3f and ;

: disasm-copz ( w -- u )
    disasm-op 3 and ;

: disasm-uimm ( w -- u )
    $ffff and ;

: disasm-imm ( w -- n )
    disasm-uimm dup 15 rshift negate 15 lshift or ;

: disasm-relative ( addr n -- w )
    \ compute printable form of relative address n relative to addr
    2 lshift nip ( + ) ;

\ decode tables

: disasm-illegal ( addr w -- )
    \ disassemble illegal/unknown instruction w at addr
    hex. ." , ( illegal inst ) " drop ;

: disasm-table ( n "name" -- )
    \ initialize table with n entries with disasm-illegal
    create 0 ?do
	['] disasm-illegal ,
    loop
does> ( u -- addr )
    swap cells + ;

$40 disasm-table opc-tab-entry     \ top-level decode table
$40 disasm-table funct-tab-entry   \ special function table
$40 disasm-table funct-tab2-entry  \ special2 function table
$40 disasm-table funct-tab3-entry  \ special3 function table
$20 disasm-table funct-bshfl-entry  \ BSHFL function table
$20 disasm-table regimm-tab-entry  \ regim instructions rt table
$20 disasm-table copz-rs-tab-entry \ COPz instructions rs table
$20 disasm-table copz-rt-tab-entry \ COPz BC instructions rt table
$40 disasm-table cp0-tab-entry     \ COP0 CO instructions funct table

\ disassembler central decode cascade

dup set-current

: disasm-inst ( addr w -- )
    \G disassemble instruction w at addr (addr is used for computing
    \G branch targets)
    dup disasm-op opc-tab-entry @ execute ;

: disasm ( addr u -- ) \ gforth
    \G disassemble u aus starting at addr
    bounds u+do
	cr ." ( " i hex. ." ) " i i ul@ disasm-inst
	4 +loop
    cr ;

' disasm IS discode

definitions

: disasm-special ( addr w -- )
    \ disassemble inst with opcode special
    dup disasm-funct funct-tab-entry @ execute ;
' disasm-special 0 opc-tab-entry ! \ enter it for opcode special

: disasm-special2 ( addr w -- ) \ todo factor out!
    \ disassemble inst with opcode special2
    dup disasm-funct funct-tab2-entry @ execute ;
' disasm-special2 $1C opc-tab-entry ! \ enter it for opcode special

: disasm-special3 ( addr w -- ) \ todo factor out!
    \ disassemble inst with opcode special3
    dup disasm-funct funct-tab3-entry @ execute ;
' disasm-special3 $1F opc-tab-entry ! \ enter it for opcode special

: disasm-bshfl ( addr w -- )
    \ disassemble inst with opcode special3, BSHFL, function encoded in SA
    dup disasm-shamt funct-bshfl-entry @ execute ;
' disasm-bshfl $20 funct-tab3-entry ! \ enter it for function BSHFL

: disasm-regimm ( addr w -- )
    \ disassemble regimm inst
    dup disasm-rt regimm-tab-entry @ execute ;
' disasm-regimm 1 opc-tab-entry ! \ enter it for opcode regimm

: disasm-copz-rs ( addr w -- )
    \ disassemble inst with opcode COPz
    dup disasm-rs copz-rs-tab-entry @ execute ;
' disasm-copz-rs $10 opc-tab-entry ! \ enter it for opcodes COPz
' disasm-copz-rs $11 opc-tab-entry !
' disasm-copz-rs $12 opc-tab-entry !

: disasm-copz-rt ( addr w -- )
    \ disassemble inst with opcode COPz, rs=BC
    dup disasm-rt copz-rt-tab-entry @ execute ;
' disasm-copz-rt $08 copz-rs-tab-entry ! \ into COPz-table for rs=BC

: disasm-cp0 ( addr w -- )
    \ disassemble inst with opcode COPz, rs=CO
    dup disasm-funct cp0-tab-entry @ execute ;
' disasm-cp0 $10 copz-rs-tab-entry ! \ into COPz-table for rs=CO

\ dummy words for insts.fs (words with these names are needed by asm.fs)

: asm-op ( -- ) ;
: asm-rs ( -- ) ;
: asm-rt ( -- ) ;

\ disassemble various formats

: disasm-J-target ( addr w -- )
    \ print jump target
    2 lshift $0fffffff and swap $f0000000 and or hex. ;

: disasm-I-rs,rt,imm ( addr w -- )
    dup disasm-rs .
    dup disasm-rt .
    disasm-imm disasm-relative . ;

: disasm-I-rs,rel ( addr w -- )
    dup disasm-rs .
    disasm-imm disasm-relative . ;

: disasm-I-rs,imm ( addr w -- )
    dup disasm-rs .
    disasm-imm .
    drop ;
 
: disasm-rt,rs,imm ( addr w -- )
    dup disasm-rt .
    dup disasm-rs .
    disasm-imm .
    drop ;

: disasm-rt,rs,uimm ( addr w -- )
    dup disasm-rt .
    dup disasm-rs .
    disasm-uimm hex.
    drop ;

: disasm-rt,uimm ( addr w -- )
    dup disasm-rt .
    disasm-uimm hex.
    drop ;

: disasm-rt,imm,rs ( addr w -- )
    dup disasm-rt .
    dup disasm-imm .
    dup disasm-rs .
    2drop ;

: disasm-rd,rt,sa ( addr w -- )
    dup disasm-rd .
    dup disasm-rt .
    dup disasm-shamt .
    2drop ;

: disasm-rd,rt,rs ( addr w -- )
    dup disasm-rd .
    dup disasm-rt .
    dup disasm-rs .
    2drop ;

: disasm-rs. ( addr w -- )
    dup disasm-rs .
    2drop ;

: disasm-rd,rs ( addr w -- )
    dup disasm-rd .
    dup disasm-rs .
    2drop ;

: disasm-rd. ( addr w -- )
    dup disasm-rd .
    2drop ;

: disasm-rs,rt ( addr w -- )
    dup disasm-rs .
    dup disasm-rt .
    2drop ;

: disasm-rd,rs,rt ( addr w -- )
    dup disasm-rd .
    dup disasm-rs .
    dup disasm-rt .
    2drop ;

: disasm-rt,rd,z ( addr w -- )
    dup disasm-rt .
    dup disasm-rd .
    dup disasm-copz .
    2drop ;

: disasm-rt,rd ( addr w -- )
    dup disasm-rt .
    dup disasm-rd .
    2drop ;

: disasm-I-imm,z ( addr w -- )
    tuck disasm-imm disasm-relative .
    disasm-copz . ;

\ meta-defining word for instruction format disassembling definitions

\ The following word defines instruction-format words, which in turn
\ define anonymous words for disassembling specific instructions and
\ put them in the appropriate decode table.

: define-format ( disasm-xt table-xt -- )
    \ define an instruction format that uses disasm-xt for
    \ disassembling and enters the defined instructions into table
    \ table-xt
    create 2,
does> ( u "inst" -- )
    \ defines an anonymous word for disassembling instruction inst,
    \ and enters it as u-th entry into table-xt
    2@ swap here name string, ( u table-xt disasm-xt c-addr ) \ remember string
    noname create 2,      \ define anonymous word
    execute lastxt swap ! \ enter xt of defined word into table-xt
does> ( addr w -- )
    \ disassemble instruction w at addr
    2@ >r ( addr w disasm-xt R: c-addr )
    execute ( R: c-addr ) \ disassemble operands
    r> count type ; \ print name 

\ all the following words have the stack effect ( u "name" )
' disasm-J-target    ' opc-tab-entry 	 define-format asm-J-target
' disasm-I-rs,rt,imm ' opc-tab-entry 	 define-format asm-I-rs,rt,imm
' disasm-I-rs,rel    ' opc-tab-entry 	 define-format asm-I-rs,rel1
' disasm-rt,rs,imm   ' opc-tab-entry 	 define-format asm-I-rt,rs,imm
' disasm-rt,rs,uimm   ' opc-tab-entry 	 define-format asm-I-rt,rs,uimm
' disasm-rt,uimm      ' opc-tab-entry 	 define-format asm-I-rt,uimm
' disasm-rt,imm,rs   ' opc-tab-entry 	 define-format asm-I-rt,offset,rs
' disasm-rd,rt,sa    ' funct-tab-entry 	 define-format asm-special-rd,rt,sa
' disasm-rd,rt,rs    ' funct-tab-entry 	 define-format asm-special-rd,rt,rs
' disasm-rs.         ' funct-tab-entry 	 define-format asm-special-rs
' disasm-rd,rs       ' funct-tab-entry 	 define-format asm-special-rd,rs
' 2drop              ' funct-tab-entry 	 define-format asm-special-nothing
' disasm-rd.         ' funct-tab-entry 	 define-format asm-special-rd
' disasm-rs,rt       ' funct-tab-entry 	 define-format asm-special-rs,rt
' disasm-rd,rs,rt    ' funct-tab-entry 	 define-format asm-special-rd,rs,rt
' disasm-rd,rs       ' funct-tab2-entry  define-format asm-special2-rd,rs
' disasm-rs,rt       ' funct-tab2-entry  define-format asm-special2-rs,rt
' disasm-rd,rs,rt    ' funct-tab2-entry  define-format asm-special2-rd,rs,rt
' disasm-rt,rd       ' funct-tab3-entry  define-format asm-special3-rt,rd
' disasm-rt,rd       ' funct-bshfl-entry define-format asm-bshfl-rd,rt
' disasm-I-rs,rel    ' regimm-tab-entry  define-format asm-regimm-rs,rel
' disasm-I-rs,imm    ' regimm-tab-entry  define-format asm-regimm-rs,imm
' 2drop              ' cp0-tab-entry     define-format asm-copz0
' disasm-rt,rd,z     ' copz-rs-tab-entry define-format asm-copz-rt,rd1
' disasm-I-imm,z     ' copz-rt-tab-entry define-format asm-copz-imm1

: asm-I-rs,rel ( u1 u2 "name" -- ; compiled code: addr w -- )
    nip asm-I-rs,rel1 ;

: asm-copz-rt,rd ( u1 u2 "name" -- )
    drop asm-copz-rt,rd1 ;

: asm-copz-rt,offset,rs ( u "name" -- )
    \ ignore these insts, we disassemble using  asm-I-rt,offset,rs
    drop name 2drop ;

: asm-copz-imm ( u1 u2 u3 "name" -- )
    drop nip asm-copz-imm1 ;

include ./insts.fs

previous set-current
