2010-07-08 05:30:10 +00:00
/*
2011-10-13 01:12:10 +02:00
* Copyright ( C ) 2009 - 2011 Marcin Kościelnicki < koriakin @ 0x04 . net >
2010-07-08 05:30:10 +00:00
* All Rights Reserved .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice ( including the next
* paragraph ) shall be included in all copies or substantial portions of the
* Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
2013-03-14 01:44:05 +01:00
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR
2010-07-08 05:30:10 +00:00
* OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE ,
* ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE .
*/
2011-09-16 15:37:45 +02:00
# include "dis-intern.h"
2010-07-08 05:30:10 +00:00
2012-07-06 22:14:12 +02:00
# define F_NV40 1
2014-09-11 01:44:33 +02:00
# define F_G80 2
2012-07-06 22:33:15 +02:00
# define F_CALLRET 4
2011-09-16 15:58:45 +02:00
2010-07-08 05:30:10 +00:00
/*
2014-09-11 01:44:33 +02:00
* PGRAPH registers of interest , G80
2010-07-08 05:30:10 +00:00
*
* 0x400040 : disables subunits of PGRAPH . or at least disables access to them
* through MMIO . each bit corresponds to a unit , and the full mask
* is 0x7fffff . writing 1 to a given bit disables the unit , 0
* enables .
* 0x400300 : some sort of status register . . . shows 0xe when execution stopped ,
* 0 xX3 when running or paused .
* 0x400304 : control register . For writes :
* bit 0 : start execution
* bit 1 : resume execution
* bit 4 : pause execution
* For reads :
* bit 0 : prog running or paused
* 0x400308 : Next opcode to execute , or the exit opcode if program exitted .
* 0x40030c : RO alias of PC for some reason
* 0x400310 : PC . Readable and writable . In units of single insns .
2018-06-05 13:48:12 +02:00
* 0x40031c : register $ ra ( 20 - bit ) , holding parameters to various stuff .
2010-07-08 05:30:10 +00:00
* 0x400320 : WO , CMD register . Writing here launches a cmd , see below .
* 0x400324 : code upload index , WO . selects address to write in ctxprog code ,
* counted in whole insns .
* 0x400328 : code upload , WO . writes given insn to code segment and
* autoincrements upload address .
* 0x40032c : current channel RAMIN address , shifted right by 12 bits . bit 31 = = valid
* 0x400330 : next channel RAMIN address , shifted right by 12 bits . bit 31 = = valid
* 0x400334 : $ r register , offset in RAMIN context to read / write with opcode 1.
* in units of 32 - bit words .
2018-06-05 13:48:12 +02:00
* 0x400338 : register $ rb ( 20 - bit ) , holding parameters to various stuff .
* Used on NVAx with values 0 - 9 to
2010-07-08 05:30:10 +00:00
* select TP number to read / write with G [ 0x8000 ] . Maybe generic
* PGRAPH offset ? Also set to 0x8ffff in one place for some reason .
* 0x40033c : Some other register . Set with opcode 0x600007 . Related to and
* modified by opcode 8. Setting with opcode 0x600007 always ands
* contents with 0xffff8 for some reason .
* 0x400784 : channel RAMIN address used for memory reads / writes . needs to be
* flushed with CMD 4 after it ' s changed .
* 0x400824 : Flags , 0x00 - 0x1f , RW
* 0x400828 : Flags , 0x20 - 0x3f , RW
* 0x40082c : Flags , 0x40 - 0x5f , RO . . . this is some sort of hardware status
* 0x400830 : Flags , 0x60 - 0x7f , RW
*
* How execution works :
*
* Execution can be in one of 3 states : stopped , running , and paused .
* Initially , it ' s stopped . The only way to start execution is to poke 0x400304
* with bit 0 set . When you do this , the uc starts executing opcodes from
* current PC . The only way to stop execution is to use CMD 0xc .
* Note that CMD 0xc resets PC to 0 , so ctxprog will start over from there
* next time , unless you poke PC before that . Running < - > paused transition
* happens by poking 0x400304 with the appropriate bit set to 1. Poking with
* * both * bits set to 1 causes it to reload the opcode register from current
* PC , but nothing else happens . This can be used to read the code segment .
* Poking with bits 4 and 0 set together causes it to start with paused state .
*
* Oh , also , PC has 9 valid bits . So you can have ctxprogs of max . 512 insns .
*
* CMD : These are some special operations that can be launched either from
* host by poking the CMD number to 0x400320 , or by ctxprog by running opcode
* 6 with the CMD number in its immediate field . See tabcmd .
*
*/
/*
* PGRAPH registers of interest , NV40
*
* 0x400040 : disables subunits of PGRAPH . or at least disables access to them
* through MMIO . each bit corresponds to a unit , and the full mask
* is 0x7fffff . writing 1 to a given bit disables the unit , 0
* enables .
* 0x400300 : some sort of status register . . . shows 0xe when execution stopped ,
* 0 xX3 when running or paused .
* 0x400304 : control register . Writing 1 startx prog , writing 2 kills it .
* 0x400308 : Current PC + opcode . PC is in high 8 bits , opcode is in low 24 bits .
* 0x400310 : Flags 0 - 0xf , RW
* 0x400314 : Flags 0x20 - 0x2f , RW
* 0x400318 : Flags 0x60 - 0x6f , RO , hardware status
* 0x40031c : the scratch register , written by opcode 2 , holding params to
* various stuff .
* 0x400324 : code upload index , RW . selects address to write in ctxprog code ,
* counted in whole insns .
* 0x400328 : code upload . writes given insn to code segment and
* autoincrements upload address . RW , but returns something crazy on
* read
* 0x40032c : current channel RAMIN address , shifted right by 12 bits . bit 24 = = valid
* 0x400330 : next channel RAMIN address , shifted right by 12 bits . bit 24 = = valid
* 0x400338 : Some register , set with opcode 3
* 0x400784 : channel RAMIN address used for memory reads / writes . needs to be
* flushed with CMD 4 after it ' s changed .
*
*/
/*
* Code target field
*
* This field represents a code address and is used for branching and the
* likes . Target is counted in 32 - bit words from the start of microcode .
*/
2012-08-02 03:48:53 +02:00
static struct rbitfield ctargoff = { 8 , 9 } ;
2010-10-21 15:32:47 +00:00
# define BTARG atombtarg, &ctargoff
# define CTARG atomctarg, &ctargoff
2010-07-08 05:30:10 +00:00
2010-10-26 13:06:08 +00:00
/* Register fields */
2018-06-05 13:48:12 +02:00
static struct reg ra_r = { 0 , " ra " } ;
static struct reg rb_r = { 0 , " rb " } ;
# define RA atomreg, &ra_r
# define RB atomreg, &rb_r
2010-10-26 13:06:08 +00:00
2010-07-08 05:30:10 +00:00
/*
* Misc number fields
*
* Used for plain numerical arguments .
*/
2018-06-05 13:48:12 +02:00
static struct bitfield scratchbitoff = { 0 , 5 } ;
2010-10-21 13:37:51 +00:00
static struct bitfield flagoff = { 0 , 7 } ;
static struct bitfield gsize4off = { 14 , 6 } ;
static struct bitfield gsize5off = { 16 , 4 } ;
static struct bitfield immoff = { 0 , 20 } ;
2018-06-05 13:48:12 +02:00
static struct bitfield rmaskoff = { 0 , 16 } ;
static struct bitfield ridxoff = { 16 , 4 } ;
static struct bitfield xstart4off = { 3 , 7 } ;
static struct bitfield xstart5off = { 8 , 11 } ;
static struct bitfield xmaskoff = { 0 , 8 } ;
2010-10-28 13:19:34 +00:00
static struct bitfield cmdoff = { 0 , 5 } ;
2018-06-05 13:48:12 +02:00
# define SCRATCHBIT atomimm, &scratchbitoff
2010-10-21 13:37:51 +00:00
# define FLAG atomimm, &flagoff
# define GSIZE4 atomimm, &gsize4off
# define GSIZE5 atomimm, &gsize5off
# define IMM atomimm, &immoff
2018-06-05 13:48:12 +02:00
# define RMASK atomimm, &rmaskoff
# define RIDX atomimm, &ridxoff
# define XSTART4 atomimm, &xstart4off
# define XSTART5 atomimm, &xstart5off
# define XMASK atomimm, &xmaskoff
2010-10-28 13:19:34 +00:00
# define CMD atomimm, &cmdoff
2010-07-08 05:30:10 +00:00
/*
* Memory fields
*/
// BF, offset shift, 'l'
2012-08-02 03:48:53 +02:00
static struct rbitfield pgmem4_imm = { { 0 , 14 } , RBF_UNSIGNED , 2 } ;
static struct rbitfield pgmem5_imm = { { 0 , 16 } , RBF_UNSIGNED , 2 } ;
2010-10-26 13:40:10 +00:00
static struct mem pgmem4_m = { " G " , 0 , 0 , & pgmem4_imm } ;
static struct mem pgmem5_m = { " G " , 0 , 0 , & pgmem5_imm } ;
# define PGRAPH4 atommem, &pgmem4_m
# define PGRAPH5 atommem, &pgmem5_m
2010-07-08 05:30:10 +00:00
2010-07-08 06:23:29 +00:00
static struct insn tabrpred [ ] = {
2018-06-05 13:48:12 +02:00
{ 0x00 , 0x7f , N ( " dirsave " ) } , // the direction flag; 0: read from memory, 1: write to memory
{ 0x03 , 0x7f , N ( " ctxfreeze " ) , . fmask = F_G80 } , // disallows all modification of context, so that S/R can proceed
{ 0x05 , 0x7f , N ( " swsavereq " ) } , // SW defined
{ 0x06 , 0x7f , N ( " swrestorereq " ) } , // SW defined
{ 0x09 , 0x7f , N ( " swxferreq " ) } , // SW defined
{ 0x1c , 0x7f , N ( " pm0 " ) , . fmask = F_G80 } ,
{ 0x1d , 0x7f , N ( " pm1 " ) , . fmask = F_G80 } ,
{ 0x1e , 0x7f , N ( " pm2 " ) , . fmask = F_G80 } ,
{ 0x1f , 0x7f , N ( " pm3 " ) , . fmask = F_G80 } ,
// NV40 hardware RO flags
{ 0x60 , 0x7f , N ( " idle " ) , . fmask = F_NV40 } ,
{ 0x64 , 0x7f , N ( " hwsavereq " ) , . fmask = F_NV40 } ,
{ 0x65 , 0x7f , N ( " hwrestorereq " ) , . fmask = F_NV40 } ,
{ 0x66 , 0x7f , N ( " state3d " ) , . fmask = F_NV40 } , // wired straight to the CTX_USER bit.
{ 0x68 , 0x7f , . fmask = F_NV40 } , // always
{ 0x69 , 0x7f , N ( " timeridle " ) , . fmask = F_NV40 } ,
// G80 hardware RO flags
{ 0x40 , 0x7f , N ( " idle " ) , . fmask = F_G80 } ,
{ 0x44 , 0x7f , N ( " hwsavereq " ) , . fmask = F_G80 } ,
{ 0x45 , 0x7f , N ( " hwrestorereq " ) , . fmask = F_G80 } ,
{ 0x4a , 0x7f , N ( " binddmaack " ) , . fmask = F_G80 } , // bind ctx dma CMD finished with loading new address... or something like that, it seems to be turned back off *very shortly* after the CMD. only check it with a wait right after cmd. weird.
2014-09-11 01:44:33 +02:00
{ 0x4b , 0x7f , N ( " xferbusy " ) , . fmask = F_G80 } , // RAMIN xfer in progress
2018-06-05 13:48:12 +02:00
{ 0x4c , 0x7f , N ( " timeridle " ) , . fmask = F_G80 } ,
2014-09-11 01:44:33 +02:00
{ 0x4d , 0x7f , . fmask = F_G80 } , // always
2018-06-05 13:48:12 +02:00
{ 0x4f , 0x7f , N ( " intrpending " ) , . fmask = F_G80 } ,
// G80 scratch reg
{ 0x60 , 0x60 , N ( " scratch " ) , SCRATCHBIT , . fmask = F_G80 } ,
// unknown
{ 0x00 , 0x00 , N ( " flag " ) , FLAG , OOPS } ,
2010-07-08 05:30:10 +00:00
} ;
2010-07-08 06:23:29 +00:00
static struct insn tabpred [ ] = {
2010-12-26 14:05:57 +00:00
{ 0x80 , 0x80 , N ( " not " ) , T ( rpred ) } ,
{ 0x00 , 0x80 , T ( rpred ) } ,
{ 0 , 0 , OOPS } ,
2010-07-08 05:30:10 +00:00
} ;
2018-06-05 13:48:12 +02:00
static struct insn tabcmd4 [ ] = {
{ 0x07 , 0x1f , C ( " BIND_CHANNEL " ) } , // copies 330 [new channel] to 784 [channel used for ctx RAM access]
{ 0x09 , 0x1f , C ( " FINISH_CHSW " ) } , // movs new channel RAMIN address to current channel RAMIN address, making PFIFO happy
{ 0x0a , 0x1f , C ( " SET_CTX_POINTER " ) } , // copies scratch to 334
{ 0x0b , 0x1f , C ( " START_TIMER " ) } , // sets the timer to count down $ra cycles
{ 0x0c , 0x1f , C ( " CLEAR_RESET " ) } , // undoes all reset commands
{ 0x0d , 0x1f , C ( " HALT " ) } , // halt execution *without* resetting PC to 0
{ 0x0e , 0x1f , C ( " END " ) } , // halts program execution, resets PC to 0
{ 0x0f , 0x1f , C ( " INTR " ) } , // trigger context switch interrupt and halt
{ 0 , 0 , OOPS , CMD } ,
} ;
2010-07-08 06:23:29 +00:00
static struct insn tabcmd5 [ ] = {
2018-06-05 13:48:12 +02:00
{ 0x04 , 0x1f , C ( " BIND_CTX_DMA " ) } , // rebinds DMA object for ctx access
{ 0x05 , 0x1f , C ( " BIND_CHANNEL " ) } , // copies 330 [new channel] to 784 [channel used for MMU accesses]
{ 0x06 , 0x1f , C ( " SET_REG_POINTER " ) } , // copies scratch to 334
{ 0x07 , 0x1f , C ( " SET_XFER_POINTER " ) } , // copies scratch to 33c, anding it with 0xffff8
{ 0x08 , 0x1f , C ( " START_TIMER " ) } ,
{ 0x09 , 0x1f , C ( " CLEAR_RESET " ) } ,
{ 0x0a , 0x1f , C ( " HALT " ) } ,
{ 0x0b , 0x1f , C ( " INTR " ) } ,
{ 0x0c , 0x1f , C ( " END " ) } ,
{ 0x0d , 0x1f , C ( " FINISH_CHSW " ) } ,
// 0x11 seen
// 0x16 seen on G200+
{ 0 , 0 , OOPS , CMD } ,
2010-07-08 05:30:10 +00:00
} ;
2018-06-05 13:48:12 +02:00
static struct insn tabxarea4 [ ] = {
{ 0x000001 , 0x000007 , N ( " main " ) } ,
{ 0x000002 , 0x000007 , N ( " unk2 " ) } ,
{ 0 , 0 , OOPS } ,
} ;
static struct insn tabxarea5 [ ] = {
{ 0x000000 , 0x080000 , N ( " main " ) } ,
{ 0x080000 , 0x080000 , N ( " mp " ) } ,
{ 0 , 0 , OOPS } ,
2010-07-08 05:30:10 +00:00
} ;
2010-07-08 06:23:29 +00:00
static struct insn tabm [ ] = {
2018-06-05 13:48:12 +02:00
{ 0x000000 , 0xf00000 , N ( " nop " ) } ,
{ 0x100000 , 0xff0000 , N ( " reg " ) , PGRAPH5 , RA , . fmask = F_G80 } ,
{ 0x100000 , 0xf00000 , N ( " reg " ) , PGRAPH5 , GSIZE5 , . fmask = F_G80 } ,
{ 0x100000 , 0xffc000 , N ( " reg " ) , PGRAPH4 , RA , . fmask = F_NV40 } ,
{ 0x100000 , 0xf00000 , N ( " reg " ) , PGRAPH4 , GSIZE4 , . fmask = F_NV40 } ,
{ 0x200000 , 0xf00000 , N ( " li " ) , RA , IMM } , // moves 20-bit immediate to $ra
{ 0x300000 , 0xf00000 , N ( " li " ) , RB , IMM } , // moves 20-bit immediate to $rb
{ 0x400000 , 0xfc0000 , N ( " jmp " ) , T ( pred ) , BTARG } , // jumps if condition true
2012-07-06 22:33:15 +02:00
{ 0x440000 , 0xfc0000 , N ( " call " ) , T ( pred ) , CTARG , . fmask = F_CALLRET } , // calls if condition true
{ 0x480000 , 0xfc0000 , N ( " ret " ) , T ( pred ) , . fmask = F_CALLRET } , // rets if condition true
2018-06-05 13:48:12 +02:00
{ 0x500000 , 0xf00000 , N ( " waitfor " ) , T ( pred ) } , // waits until condition true.
2014-09-11 01:44:33 +02:00
{ 0x600000 , 0xf00000 , N ( " cmd " ) , T ( cmd5 ) , . fmask = F_G80 } , // runs a CMD.
2012-07-06 22:14:12 +02:00
{ 0x600000 , 0xf00000 , N ( " cmd " ) , T ( cmd4 ) , . fmask = F_NV40 } , // runs a CMD.
2018-06-05 13:48:12 +02:00
{ 0x700000 , 0xf00080 , N ( " clear " ) , T ( rpred ) } , // clears given flag
{ 0x700080 , 0xf00080 , N ( " set " ) , T ( rpred ) } , // sets given flag
{ 0x800000 , 0xf00000 , N ( " xfer " ) , T ( xarea4 ) , XSTART4 , . fmask = F_NV40 } ,
{ 0x800000 , 0xf00000 , N ( " xfer " ) , T ( xarea5 ) , XMASK , . fmask = F_G80 } ,
{ 0x900000 , 0xf00000 , N ( " reset " ) , RIDX , RMASK } , // ors 0x40 with given immediate.
{ 0xa00000 , 0xf00000 , N ( " ldsr " ) , PGRAPH5 , . fmask = F_G80 } , // movs given PGRAPH register to 0x400830 aka the scratch reg.
{ 0xc00000 , 0xf00000 , N ( " seek " ) , T ( xarea5 ) , XMASK , XSTART5 , . fmask = F_G80 } ,
2010-12-26 14:05:57 +00:00
{ 0 , 0 , OOPS } ,
2010-07-08 05:30:10 +00:00
} ;
2010-10-19 19:44:34 +00:00
static struct insn tabroot [ ] = {
2012-08-05 16:51:49 +02:00
{ 0 , 0 , OP1B , T ( m ) } ,
2010-10-19 19:44:34 +00:00
} ;
2010-07-08 05:30:10 +00:00
2012-07-06 22:14:12 +02:00
static void ctx_prep ( struct disisa * isa ) {
isa - > vardata = vardata_new ( " ctxisa " ) ;
int f_nv40op = vardata_add_feature ( isa - > vardata , " nv40op " , " NV40 family exclusive opcodes " ) ;
2014-09-11 01:44:33 +02:00
int f_g80op = vardata_add_feature ( isa - > vardata , " g80op " , " G80 family exclusive opcodes " ) ;
2012-07-06 22:14:12 +02:00
int f_callret = vardata_add_feature ( isa - > vardata , " callret " , " call and ret ops " ) ;
2014-09-11 01:44:33 +02:00
if ( f_nv40op = = - 1 | | f_g80op = = - 1 | | f_callret = = - 1 )
2012-07-06 22:14:12 +02:00
abort ( ) ;
int vs_chipset = vardata_add_varset ( isa - > vardata , " chipset " , " GPU chipset " ) ;
if ( vs_chipset = = - 1 )
abort ( ) ;
2014-09-11 01:44:33 +02:00
int v_nv40 = vardata_add_variant ( isa - > vardata , " nv40 " , " NV40:G80 " , vs_chipset ) ;
int v_g80 = vardata_add_variant ( isa - > vardata , " g80 " , " G80:G200 " , vs_chipset ) ;
2014-09-11 01:10:08 +02:00
int v_g200 = vardata_add_variant ( isa - > vardata , " g200 " , " G200:GF100 " , vs_chipset ) ;
2014-09-11 01:44:33 +02:00
if ( v_nv40 = = - 1 | | v_g80 = = - 1 | | v_g200 = = - 1 )
2012-07-06 22:14:12 +02:00
abort ( ) ;
vardata_variant_feature ( isa - > vardata , v_nv40 , f_nv40op ) ;
2014-09-11 01:44:33 +02:00
vardata_variant_feature ( isa - > vardata , v_g80 , f_g80op ) ;
vardata_variant_feature ( isa - > vardata , v_g200 , f_g80op ) ;
2014-09-11 01:10:08 +02:00
vardata_variant_feature ( isa - > vardata , v_g200 , f_callret ) ;
2012-07-06 22:31:05 +02:00
if ( vardata_validate ( isa - > vardata ) )
2012-07-06 22:14:12 +02:00
abort ( ) ;
}
2011-09-16 15:58:45 +02:00
2012-07-06 22:14:12 +02:00
struct disisa ctx_isa_s = {
2010-10-19 19:44:34 +00:00
tabroot ,
2012-08-05 16:44:48 +02:00
1 ,
1 ,
2010-10-19 19:44:34 +00:00
4 ,
2012-07-06 22:14:12 +02:00
. prep = ctx_prep ,
2010-10-19 19:44:34 +00:00
} ;