Kodewerx

Our culture has advanced beyond all that you could possibly comprehend with one hundred percent of your brain.
It is currently Sat Jan 18, 2020 12:52 pm

All times are UTC - 8 hours [ DST ]




Post new topic Reply to topic  [ 18 posts ] 
Author Message
 Post subject: N64 DMA
PostPosted: Tue Feb 15, 2011 5:13 am 
Offline
Komrade
Komrade

Joined: Tue Mar 27, 2007 10:18 am
Posts: 1328
Here are the notes I have on N64 DMA...the ones I found were incorrect/had information missing.

"The trainer executes the code at a bad time and the code must be executed at a certain time and/or under certain conditions and therefore crashes the game after reading the save."

Quote:
Code:
.set            noreorder

;-------------------------------;
;   BEGIN CHEATS      ;
;-------------------------------;

;   Save load test
.long            0xC0DE0000
.long            0x00000000

LUI   S0,   0xA460      ;0xA4600000
LUI   S1,   0x000B      ;
ORI   S1,   S1,   0x6F80   ;0x000B6F80
LUI   S2,   0x0800      ;0x08000000
ORI   S3,   R0,   0x007F   ;0x0000007F
ORI   S4,   R0,   0x0002   ;0x00000002

            Wait_Loop:
LW   S5,   0x0010   (S0)   ;
ANDI   S5,   S5,   0x0003   ;
BNEZ   S5,         Wait_Loop
NOP

SW   S1,   0x0000   (S0)   ;
SW   S2,   0x0004   (S0)   ;
SW   S3,   0x000C   (S0)   ;
SW   S4,   0x0010   (S0)   ;

            Exit:
JR   RA         ;
NOP

.align            3
.long            0xE0000000
.long            0x00000000
;   End of save load test

;-------------------------------;
;   END OF CHEAT LIST   ;
;-------------------------------;

.long            0xFFFFFFFF
.long            0xFFFFFFFF

Excerpt from

http://en.wikibooks.org/wiki/N64_Progra ... ry_mapping

regarding N64 DMA (note that the below has been modified from how it was originally found at the link above to be more accurate):

Code:
DMA registers:

    $A460:0000 = RAM address (address & 0x00FFFFFF)
    $A460:0004 = ROM address (address & 0x1FFFFFFF)
    $A460:0008 = Transfer size (from RAM to cartridge)
    $A460:000C = Transfer size (from cartridge to RAM)
    $A460:0010 = DMA Status

The last two addresses accept the DMA copy length (minus 1--BPL loop) and start the transfer. Which length register you write to depends on the transfer direction you want. Once the transfer has been initiated, the status of the transfer can be checked by reading the status register. The following flags can be used to check the status:

    * DMA_BUSY  = 0x00000001
    * DMA_DONE  = 0x00000002 <- This is written rather than read to signal the end of inputting transfer arguments
    * DMA_ERROR = 0x00000008

Below is some example C code to utilize the N64's DMA functions:

/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **
** N64 DMA                         **
** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

typedef struct {
   /* Pointers to data */
   void* ramp;
   void* romp;
 
   /* Filesizes (8-byte aligned) */
   u32 size_ramrom; /* RAM -> ROM */
   u32 size_romram; /* RAM <- ROM */
 
   /* Status register */
   u32 status;
} DMA_REG;
 
/* DMA status flags */
enum {
   DMA_BUSY  = 0x00000001,
   DMA_DONE  = 0x00000002,
   DMA_ERROR = 0x00000008
};
 
/* DMA registers ptr */
static volatile DMA_REG* dmaregs = (DMA_REG*)0xA4600000;
 
/* Copy data from ROM to RAM */
int dma_write_ram (void* ram_ptr, void* rom_ptr, u32 length) {
   /* Check that DMA is not busy already */
   while (dmaregs->status & (DMA_BUSY|DMA_DONE));
 
   /* Write addresses */
   dmaregs->ramp = (u32)ram_ptr & 0x00FFFFFF; /* ram pointer */
   dmaregs->romp = (u32)rom_ptr & 0x1FFFFFFF; /* rom pointer */
 
   /* Write size */
   dmaregs->size_romram = length - 1;
 
   /* Tell hardware to process request **
   ** If this is not done the DMA hardware will not function later **
   ** at least for the memory that wasn't fully accessed */
   dmaregs->status = DMA_DONE;
 
   /* Return size written */
   return length & 0xFFFFFFF8;
}

_________________
Image


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Sat Mar 05, 2011 4:45 pm 
Offline
Komrade
Komrade

Joined: Tue Mar 27, 2007 10:18 am
Posts: 1328
Turns out DMA triggers an interrupt and my trainer executes in the exception handler, so it was causing infinite recursion. This code worked:

Code:
.set            noreorder

;-------------------------------;
;   BEGIN CHEATS      ;
;-------------------------------;

;   Save load code
.long            0xC0DE0000
.long            0x00000000

LUI   S1,   0x800B      ;Only load save if it hasn't been loaded
            ;This read's purpose is explained by
            ;a write to this address which occurs later in the code
LW   S1,   0x6AC4   (S1)   ;[0x800B6AC4] == 0?
BEQZ   S1,         Exit
NOP

LUI   S1,   0x8010      ;
LH   S2,   0x5304   (S1)   ;
XORI   S2,   S2,   0x0020   ;Check L button; load if only L pressed
BNEZ   S2,         Exit
NOP

LUI   S0,   0xA460      ;0xA4600000
LUI   S1,   0x000B      ;
ORI   S1,   S1,   0x6AC8   ;0x000B6AC8; 0x800B6AC8 is where the save will be loaded to
LUI   S2,   0x0800      ;0x08000000; Flash base address
            ;(other saves at save# * 0x4000; 6 in all starting with #0)
ORI   S3,   R0,   0x0537   ;0x00000537; Saves are 0x537 + 1 bytes in size
;ORI   S4,   R0,   0x0002   ;0x00000002; this does not need to be written
            ;to 0xA4600010 like usual because the exception
            ;handler the game initalizes will do this anyway
            ;(incidentally this is why this code causes
            ;a recursion crash without the semaphore
            ;implemented above)

            Wait_Loop:
LW   S5,   0x0010   (S0)   ;
ANDI   S5,   S5,   0x0003   ;
BNEZ   S5,         Wait_Loop
NOP

LUI   S4,   0x800B      ;This will prevent the exception handler
            ;from calling this code again recursively
            ;(this code is executed in the exception handler
            ;which is triggered when DMA is done,
            ;such as by writing to 0xA460000C like in this case)
            ;based on a check performed earlier in this code
SW   R0,   0x6AC4   (S4)   ;[0x800B6AC4] = 0
SW   S1,   0x0000   (S0)   ;RAM
SW   S2,   0x0004   (S0)   ;Hardware
SW   S3,   0x000C   (S0)   ;Size

            Exit:
JR   RA         ;
NOP

.align            3
.long            0xE0000000
.long            0x00000000
;   End of save load test

;-------------------------------;
;   END OF CHEAT LIST   ;
;-------------------------------;

.long            0xFFFFFFFF
.long            0xFFFFFFFF

_________________
Image


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Sat Mar 05, 2011 7:34 pm 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
I still don't understand what this is for ...

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Sun Mar 06, 2011 8:42 am 
Offline
Komrade
Komrade

Joined: Tue Mar 27, 2007 10:18 am
Posts: 1328
MIPS code to read the flash of a commercial game in the event you want to make a ROM hack that works with the game's save data?

_________________
Image


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Sun Mar 06, 2011 3:41 pm 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
Yes, but, why is it running in the interrupt handler? Are you using this in a GS code? By the looks of your first post, I thought it was something to do with the Banjo Tooie crashing problems with GS Pro + code generator.

Anyway, it seems to me a more valuable place to attack the save data is by hooking the code that actually reads and writes it...

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Sun Mar 06, 2011 8:59 pm 
Offline
Komrade
Komrade

Joined: Tue Mar 27, 2007 10:18 am
Posts: 1328
That code doesn't execute in a place that doesn't cause desynchronization when playing online for my purposes.

Also I tried hooking the code that does DMA with the ROM and it failed.

My code is executing in the exception handler because I put my trainer there. It was an easy way to be sure my code was always executed. If I need my code executed constantly but at another time, there's the 0x87 code type for that too.

I posted about the specifications for my trainer elsewhere I believe. I've only done it for Jet Force Gemini thus far.

Code:
Legend:
I - Increment (amount to increment data)
M - Mask
N - Count
O - Offset (increment amount for address)
X - Address
Y - Data
/ - Ignored; should be replaced with 0 for clarity in most cases
(access type) - Access type used by implementing instructions

~~~~~
Write codes
~~~~~

80XXXXXX //////YY - Writes [data] to [address] (byte)
81XXXXXX ////YYYY - "" (halfword)
82XXXXXX YYYYYYYY - "" (word)
83XXXXXX YYYYYYYY - Data at [Address] is set to the contents of [address] OR [data] (word)
84XXXXXX YYYYYYYY - Data at [Address] is set to the contents of [address] AND [data] (word)
85XXXXXX NNNNNNNN - Writes the following ([count] * 8) bytes to [address] (dword)
86XXXXXX YYYYYYYY - Serial repeater; first line of two (second will be skipped if not followed by line two)
   Writes [data] to [count] addresses with [offset] incrementing the address each time (signed)
   [Address] is being written to with [data] incremented by [increment] each time [data] is written (signed)
   See code type 9 for info on the format of [count] and [increment]
   (Access type is determined by [mask]; [mask] & 3 == 0: byte, == 1: halfword, == 2: word, == 3: byte)
9//MNNNN OOOOIIII - Serial repeater; second line of two (will be skipped by code handler and processed inline with codes of the type that begin serial repeaters)
87XXXXXX //////// - Writes a J instruction at [address] that will jump to the next line of code for execution
   Skip following lines until after "Terminator code" is encountered*

* Only 1 terminator code is searched for

~~~~~
Condition codes
~~~~~

D0XXXXXX //////YY - "Do if equal" - Skip following lines until after "Terminator code" is encountered* if [data] is not equal to contents at address (byte)
D1XXXXXX ////YYYY - "" (halfword)
D2XXXXXX YYYYYYYY - "" (word)
D3XXXXXX MMMMYYYY - "Do if equal" - Skip following lines until after "Terminator code" is encountered* if [data] is not equal to contents at [address] AND [mask] (halfword)
D4XXXXXX //////YY - "Do if not equal" - Skip following lines until after "Terminator code" is encountered* if [data] is equal to contents at address (byte)
D5XXXXXX ////YYYY - "" (halfword)
D6XXXXXX YYYYYYYY - "" (word)
D7XXXXXX MMMMYYYY - "Do if not equal" - Skip following lines until after "Terminator code" is encountered* if [data] is equal to contents at [address] AND [mask] (halfword)
D8XXXXXX //////YY - "Do if less" - Skip following lines until after "Terminator code" is encountered* if [data] is greater than or equal to the contents at address (byte)
D9XXXXXX ////YYYY - "" (halfword)
DAXXXXXX YYYYYYYY - "" (word)
DB////// //////// - Not implemented
DCXXXXXX //////YY - "Do if more" - Skip following lines until after "Terminator code" is encountered* if [data] is less than or equal to the contents at address (byte)
DDXXXXXX ////YYYY - "" (halfword)
DEXXXXXX YYYYYYYY - "" (word)
DF////// //////// - Not implemented
E0000000 //////// - Terminator code

* Number of terminator codes that must be encountered starts at 1 and increases each time another conditional code is encountered in between

~~~~~
Special codes
~~~~~

C/////// //////// - Stores V0 and RA, sets RA to return to code handler and jumps to the next 8 byte aligned code for execution;
   Restores V0 and RA when called code returns with JR RA
   Advances interpreter pointer until after "Terminator code" is encountered*
   Recommended appearance: C0DE0000 00000000
FFFFFFFF //////// - Code handler terminator; when this is reached, the code handler returns execution to the game
   This interrupts searches for JR RA opcodes and conditional terminators in addition to searches for codes
   Recommended appearance: FFFFFFFF FFFFFFFF

* Only 1 terminator code is searched for

As a note, the reason 0xC/////// only stores V0 and RA is because all other GPRs are stored and later restored already by other code.

If I'd done it with GoldenEye I'd have added TLB code support in the form of 0x7NXXXXXX like you suggested elsewhere...

_________________
Image


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Sun Mar 06, 2011 9:33 pm 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
I'm not sure how to read that first statement; I don't work well with double-negatives. Essentially what you said is, "That code executes in a place that causes desynchronization when playing online ..." Whatever "That code" might refer to? And what "reading the contents of EEPROM" has to do with "desynchronization while playing online" is beyond me! Maybe it would be best if you posted some background information for this project, so I'm not so terribly confused.

On the topic of code types: While not as compact, I always liked the idea of keeping code types separate from the address and data. Especially handy on architectures with multiple interesting address maps. And N64 qualifies, because virtual memory is very interesting, indeed. The other, perhaps more obvious, option is to just use assembled instructions directly. It's a bit more verbose, for certain. But at the same time, much more powerful.

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Mon Mar 07, 2011 9:12 am 
Offline
Komrade
Komrade

Joined: Tue Mar 27, 2007 10:18 am
Posts: 1328
There IS the 0xC code type and the 0x87 code type. There could be an 0x77 code type if I wanted one.

Basically the single player menu in Jet Force Gemini is the only part of the game where save data is loaded. It's loaded all at once when you first enter the menu and only then, then cached until it's needed when a select part of it is moved to the area that is actually written back to the flash (I'm calling it flash because the extension for the save is .fla, not .eep).

However when you enter this menu while playing the game online it desyncs. I have no idea why. I've noticed that playing Diddy Kong Racing's single player mode will cause desynchronization if you stay in the main lobby too long, but not if you play with other players using the JOINTVENTURE code and get to a race quickly. It's almost like the Kaillera client poorly handles not receiving input from the second player.

So like I said...the code "doesn't execute in a place I can use", and a place I can use is "a place where the game doesn't desync".

I already knew the code loaded saves when executed directly using the 0xC code type and then determined that it was crashing because of recursion...problem solved.

So I guess the summary is both what I put in the first post (read the status and wait until it's not "busy", write the RAM, ROM and length registers) and this addendum: instead of writing the value "0x2" to the status register afterward, expect the exception handler to do it for you if you're working with a commercial game.

I'm sure anyone with a hack idea that entails saving more data than the game usually does is probably doing so because of some new feature they've added to a game. This information could be useful for a hack like that and any hacks like that are bound to be above average.

_________________
Image


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Fri Mar 11, 2011 9:03 pm 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
That makes a little more sense. Can I ask though, why not a ROM hack? You're not really limited to poking RAM any more. :)

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Sun Mar 13, 2011 5:27 pm 
Offline
Komrade
Komrade

Joined: Tue Mar 27, 2007 10:18 am
Posts: 1328
It IS a ROM hack. I just found it easier to patch just one part of the ROM and then patch the RAM after the ROM's code is loaded to there instead of hunting down the code in the RAM's original location within the ROM.

I got into hacking with ROM hacking anyway. I've done a lot more of it than I have code making!

_________________
Image


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Sun Mar 13, 2011 11:40 pm 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
Peculiar...

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Mon Mar 14, 2011 4:37 pm 
Offline
Komrade
Komrade

Joined: Tue Mar 27, 2007 10:18 am
Posts: 1328
What's peculiar? I don't understand!

_________________
Image


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Wed Mar 16, 2011 9:55 pm 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
It's just an "unclean" way of doing a ROM hack. Nothing wrong with it, I just don't approve, personally.

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Wed Mar 16, 2011 10:00 pm 
Offline
Komrade
Komrade

Joined: Tue Mar 27, 2007 10:18 am
Posts: 1328
Yeah, it does look kind of dirty...

Consider this, though:

It's perfect for making short little cheats permanent to the ROM. It's a trainer, after all; that's what it's for.

Lots of the hacks I'm using it for require new code to be added (and were originally GameShark codes; see above). This means free space located in the ROM to put the code and some code for loading it - exactly what the trainer does. On top of that, a large majority of them don't actually need to be hooked from any particular routine...they just need to be constantly executed somehow. The 0xC code type is brilliant for this. Actually, I'm really happy with how it and the 0x87 code type both turned out - they trivialize the process of hooking code into the game's execution flow to a point that I'm not confident it could be an easier.

It's really not that strange, I say.

To top all of that off, it was a really fun project.

Then I realized...the D3D plugin I'm using for Project 64, the only one that correctly supports the game, does so by reading the addresses 0x10 and 0x14 in the ROM and comparing the checksums there to an internal list. Where the "keys" match, a value is specified which is used to indicate which settings to use, allowing the plugin to work correctly for the game. The trainer hack edits some code within the checksum protected area and requires those checksums to be modified...thus causing the plugin to no longer recognize the game and work correctly.

I have determined an ugly hack for fixing the issue in Project 64 but would rather not have to deal with it. I even tried hacking the plugin itself but the DLL seems to be mangled according to IDA Pro. Suggestions about how to resolve the issue would be nice, though I don't expect anything can really be done about it.

Except what I'm trying now: I wrote a program that looks for "ADDIU SP, SP, $XXXX" instructions and then finds corresponding instructions representing the beginnings and endings of functions. It goes through and, every time it finds a "valid" pair, it replaces the beginning of the function with an "ID" for that function and a branch to that ID just after it, causing an infinite loop. It also gives a log of the modifications it does. Then it's a simple matter of looking at Project 64's (I use PJ because of compatibility) value for the program counter while running the game via the interpreter core (for program counter accuracy) to see where it crashes and going to that address to cross reference the ID with the data in the log.

I now know of a function that is both executed soon within the game's execution flow after initialization that is also outside of the checksum protected area (which I determined the length of earlier) that I can potentially use to get around the checksum/plugin issue. Interestingly the code is modified after it is loaded into the RAM, presumably due to being relocatable. I had to compare the footprint of the code in the RAM with that of it in the ROM to find a string of bytes between branches which wasn't modified after "translation" which would be long enough to embed a loader stub. I expect I can hack up a solution with some unfortunately tedious work soon...

How the hell is this usually done, if it's ever done without the checksums being modified, anyway?

_________________
Image


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Wed Mar 16, 2011 10:35 pm 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
This is why high level emulation is bad. The video plugin is broken, and you shouldn't have to workaround it in your hack. Best to just patch the DLL. It might be compressed or "protected" in some way, but it's seriously trivial to unpack just about anything using ollydbg. Actually, it might just be UPX ... Download it and try to unpack with UPX. If it works, hey presto! Otherwise, ollydbg can usually get you a valid dump if it can locate the proper entry point.

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Thu Mar 17, 2011 6:41 pm 
Offline
Komrade
Komrade

Joined: Tue Mar 27, 2007 10:18 am
Posts: 1328
olly doesn't seem to find anything IDA couldn't. It's still showing something else at the RAM address where the list normally is, and even if the list was there so that I could hack it, how would I convert my changes into a DLL modification?

If I had the fucking source code maybe I could fix it that way...

I did a binary search for the entry in the list of interest (B6 09 60 8A 50 E1 AC 94 26 00 00 00 00 00 00 00, the checksums followed by the "settings" for the game (the 0x26)) and didn't find anything in the entire set of memory being debugged.

Edit: Oh I remember now

Yeah, it's not there to begin with. The data is copied to there from another memory segment.

A memory segment which unfortunately is not there at initialization, which makes setting a break on writes to it a pain in the ass. I can't figure out how that memory segment or its contents get there.

_________________
Image


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Fri Mar 18, 2011 5:56 pm 
Offline
Komrade
Komrade

Joined: Tue Mar 27, 2007 10:18 am
Posts: 1328
Aha! I found a plugin that works if proper settings are chosen. It still has some glitches but nothing terrible. It works for bosses so it should work for the rest of the game, although the README included with my hack now says to just switch between plugins as necessary.

Still, what a bunch of BS. From what I've seen it appears it's a frame buffer issue, largely. Jabo's plugin doesn't enable it and that's all it really even needed to do.

Edit: "Copy Framebuffer to RDRAM". Apparently Jabo's plugins advanced settings had an option I missed. Unfortunately with Jabo's plugin, while the rest of the game still plays fine, boss fights have terrible resolution. It's possible with the option enabled to see well enough to play, unlike with the option off, but everything is especially grainy and garbled looking.

I tried looking at a RAM address which is set to the value 0x26 (the proper settings) with the customized settings and it was still 0x00 like usual...whaaaaat

_________________
Image


Top
 Profile  
Reply with quote  
 Post subject: Re: N64 DMA
PostPosted: Wed Mar 23, 2011 6:50 pm 
Offline
Kommunist
Kommunist
User avatar

Joined: Mon Oct 02, 2006 7:47 am
Posts: 336
Location: Amish Redneck Country, PA
Title: Crazy Snake
Parasyte wrote:
It's just an "unclean" way of doing a ROM hack. Nothing wrong with it, I just don't approve, personally.


True. There are times it can be useful though. Just like your crazy hook to get around Goldeneye's TLB by loading pointers from the joker hook for stuff a while back. Also consider that some parts of the code can be a bitch to simply change to suit your needs because it effects more than just what you want, like when something effect both you and enemies. Yeah, you can work around it, but you can't always do it without jumping out for more space to do a branch and shit.

_________________
Be a real programmer. Program without the .SHIT Framework.
Check out my movie collection
Quote:
<ThePhantom> What, would you prefer I keep track of it with fucking binary shifting, like you probably did? Hell no.
<ThePhantom> A hedgehog's asshole could understand my code, N-O-B-O-D-Y B-U-T Y-O-U C-A-N U-N-D-E-R-S-T-A-N-D Y-O-U-R-S
<Parasyte> Nobody has to understand it
<Parasyte> Plus, bitwise shifting owns
<Parasyte> A lot
<ThePhantom> Nobody has to understand it?
<Parasyte> Correct...
<ThePhantom> Write code like it's for your job, ass. :P
<Parasyte> No way.
<ThePhantom> Either provide fucking documentation or don't write it like a deranged circus chimp on crack.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 18 posts ] 

All times are UTC - 8 hours [ DST ]


Who is online

Users browsing this forum: Brandwatch Magpie-Crawler and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group