It is currently Thu Nov 23, 2017 10:04 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 43 posts ]  Go to page Previous  1, 2, 3  Next
Author Message
 Post subject: Re: Koei bytecode
PostPosted: Tue May 16, 2017 4:53 pm 
Offline

Joined: Mon Nov 10, 2008 3:09 pm
Posts: 431
INSTRUCTION SET

Code:
code  | nickname       | args  | operation
------+----------------+-------+-------------------------------------
00-0F | LOADL quick    |   0   | left = <quick>
10-1F | LOADR quick    |   0   | right = <quick>
20-2F | STORE quick    |   0   | <quick> = left
30-3F | PUSH quick     |   0   | push <quick>
40-4F | LOADL qimm     |   0   | left = <qimm>
50-5F | LOADR qimm     |   0   | right = <qimm>
60-6F | PUSH qimm      |   0   | push <qimm>
70-7F | ADD qimm       |   0   | left = left + <qimm>
------+----------------+-------+-------------------------------------
code  | nickname       | args  | operation
------+----------------+-------+-------------------------------------
 80   | (illegal)      |  ---  | (illegal)
 81   | LOADL near     |   1   | left = <near>
 82   | LOADL far      |   2   | left = <far>
 83   | LOADR near     |   1   | right = <near>
 84   | LOADR far      |   2   | right = <far>
 85   | STORE near     |   1   | <near> = left
 86   | STORE far      |   2   | <far> = left
 87   | PUSH near      |   1   | push <near>
 88   | PUSH far       |   2   | push <far>
 89   | BYTE LOADL imm1|   1   | left = (int8)<imm1>
 8A   | LOADL imm2     |   2   | left = <imm2>
 8B   | BYTE LOADR imm1|   1   | right = (int8)<imm1>
 8C   | LOADR imm2     |   2   | right = <imm2>
 8D   | BYTE PUSH imm1 |   1   | push (int8)<imm1>
 8E   | PUSH imm2      |   2   | push <imm2>
 8F   | BYTE ADD imm1  |   1   | left = left + (int8)<imm1>
 90   | ADD imm2       |   2   | left = left + <imm2>
91-9F | (illegal)      |  ---  | (illegal)
------+----------------+-------+-------------------------------------
code  | nickname       | args  | operation
------+----------------+-------+-------------------------------------
 A0   | BYTE LOADL far |   2   | left = (uint8)<far>
 A1   | BYTE LOADR far |   2   | right = (uint8)<far>
 A2   | BYTE STORE far |   2   | (uint8)<far> = left
 A3   | BYTE PUSH far  |   2   | push (uint8)<far>
 A4   | LOADL abs      |   2   | left = <abs>
 A5   | BYTE LOADL abs |   2   | left = (uint8)<abs>
 A6   | LOADR abs      |   2   | right = <abs>
 A7   | BYTE LOADR abs |   2   | right = (uint8)<abs>
 A8   | STORE abs      |   2   | <abs> = left
 A9   | BYTE STORE abs |   2   | (uint8)<abs> = left
 AA   | PUSH abs       |   2   | push <abs>
 AB   | BYTE PUSH abs  |   2   | push (uint8)<abs>
 AC   | CALL abs       |   2   | call <abs>
 AD   | COPY imm2      |   2   | copy <imm2> bytes from *left to *right
 AE   | UNSTACK imm1   |   1   | stackptr = stackptr + (uint8)<imm1>
 AF   | UNSTACK imm2   |   2   | stackptr = stackptr + <imm2>
------+----------------+-------+-------------------------------------
code  | nickname       | args  | operation
------+----------------+-------+-------------------------------------
 B0   | DEREF          |   0   | left = *left
 B1   | POPSTORE       |   0   | pop right; *right = left
 B2   | NOP            |   0   | no operation
 B3   | PUSHL          |   0   | push left
 B4   | POPR           |   0   | pop right
 B5   | MULT           |   0   | left = left * right
 B6   | SDIV           |   0   | left = (int16)left / (int16)right
 B7   | LONG (prefix)  |  var. | (prefix for 32-bit instructions)
 B8   | UDIV           |   0   | left = (uint16)left / (uint16)right
 B9   | SMOD           |   0   | left = (int16)left % (int16)right
 BA   | UMOD           |   0   | left = (uint16)left % (uint16)right
 BB   | ADD            |   0   | left = left + right
 BC   | SUB            |   0   | left = left - right
 BD   | LSHIFT         |   0   | left = left << right
 BE   | URSHIFT        |   0   | left = (uint16)left >> right
 BF   | SRSHIFT        |   0   | left = (int16)left >> right
------+----------------+-------+-------------------------------------
code  | nickname       | args  | operation
------+----------------+-------+-------------------------------------
 C0   | CMPEQ          |   0   | left = (left == right)
 C1   | CMPNE          |   0   | left = (left != right)
 C2   | SCMPLT         |   0   | left = ((int16)left < (int16)right)
 C3   | SCMPLE         |   0   | left = ((int16)left <= (int16)right)
 C4   | SCMPGT         |   0   | left = ((int16)left > (int16)right)
 C5   | SCMPGE         |   0   | left = ((int16)left >= (int16)right)
 C6   | UCMPLT         |   0   | left = ((uint16)left < (uint16)right)
 C7   | UCMPLE         |   0   | left = ((uint16)left <= (uint16)right)
 C8   | UCMPGT         |   0   | left = ((uint16)left > (uint16)right)
 C9   | UCMPGE         |   0   | left = ((uint16)left >= (uint16)right)
 CA   | NOT            |   0   | left = !left
 CB   | MINUS          |   0   | left = -left
 CC   | COMPL          |   0   | left = ~left
 CD   | SWAP           |   0   | swap left <-> right
 CE   | (illegal)      |  ---  | (illegal)
 CF   | RETURN         |   0   | return from function
------+----------------+-------+-------------------------------------
code  | nickname       | args  | operation
------+----------------+-------+-------------------------------------
 D0   | INC            |   0   | ++left
 D1   | DEC            |   0   | --left
 D2   | LSHIFT1        |   0   | left = left << 1
 D3   | BYTE DEREF     |   0   | left = *(uint8 *)left
 D4   | BYTE POPSTORE  |   0   | pop right; *(uint8 *)right = left
 D5   | SWITCH offs,num|2+2+tbl| contiguous switch (see below)
 D6   | JUMP abs       |   2   | instrptr = <abs>
 D7   | JUMPT abs      |   2   | if (left != 0) instrptr = <abs>
 D8   | JUMPF abs      |   2   | if (left == 0) instrptr = <abs>
 D9   | SWITCH num     | 2+tbl | noncontiguous switch (see below)
 DA   | AND            |   0   | left = left & right
 DB   | OR             |   0   | left = left | right
 DC   | XOR            |   0   | left = left ^ right
 DD   | CALLPTR        |   0   | call *left
 DE   | LEAL far       |   2   | left = &<far>
 DF   | LEAR far       |   2   | right = &<far>
------+----------------+-------+-------------------------------------
code  | nickname       | args  | operation
------+----------------+-------+-------------------------------------
 E0   | SLOADBF sz,pos |  1+1  | signed bitfield extract (see below)
 E1   | ULOADBF sz,pos |  1+1  | unsigned bitfield extract (see below)
 E2   | STOREBF sz,pos |  1+1  | bitfield insert (see below)
 E3   | JUMP back      |   1   | instrptr += <back>-256
 E4   | JUMPT back     |   1   | if (left != 0) instrptr += <back>-256
 E5   | JUMPF back     |   1   | if (left == 0) instrptr += <back>-256
 E6   | JUMP ahead     |   1   | instrptr += <ahead>
 E7   | JUMPT ahead    |   1   | if (left != 0) instrptr += <ahead>
 E8   | JUMPF ahead    |   1   | if (left == 0) instrptr += <ahead>
 E9   | CALL abs,imm1  |  2+1  | call <abs>; stackptr += (uint8)<imm1>
 EA   | CALLPTR imm1   |   1   | call *left; stackptr += (uint8)<imm1>
EB-FF | (illegal)      |  ---  | (illegal)

Switch instructions
There are two switch instructions, one ($D5) for contiguous cases and the other ($D9) for noncontiguous cases. They work exactly like a C switch statement with the left register as the variable to test.

For the contiguous switch, the first 16-bit word after the instruction is the twos-complement negative of the smallest case value. The next 16-bit word is the number of cases. Then comes the default jump target, followed by the target for each in-range value of left. Example:
Code:
.byte $D5 ; contiguous switch instruction
.word -4, 5 ; smallest case 4, 5 cases
.word DefaultTarget ; jumps to this address if left was < 4 or > 8
.word Target4 ; jumps to this address if left was 4
.word Target5 ; jumps to this address if left was 5
.word Target6 ; jumps to this address if left was 6
.word Target7 ; jumps to this address if left was 7
.word Target8 ; jumps to this address if left was 8

For the non-contiguous switch, the first 16-bit word after the instruction is the number of cases. Then comes a table of comparison values followed by jump targets. After all the value-target pairs, the default target comes last. Example:
Code:
.byte $D9 ; noncontiguous switch instruction
.word 5 ; 5 cases
.word 1, Target1
.word 4, Target4
.word 9, Target9
.word 16, Target16
.word 25, Target25
.word TargetDefault

Load effective address
Instructions $DE and $DF load a register with the value of the argument + the frame pointer, i.e. with the effective (absolute) address of a local variable. They can be used to pass a pointer to a local variable as a function argument, or in conjunction with the bitfield instructions.

Bitfields
There are three bitfield instructions: one to extract (load from) a signed bitfield, one to extract an unsigned bitfield, and one to insert (store to) a bitfield. For all three bitfield instructions, the first byte after the instruction is the size of the bitfield in bits, and the following byte is the bit position counting from the LSB. The entire bitfield must fit into a 16-bit word (i.e. size + pos < 16)

The two extraction instructions work like the dereference instruction: the address of the bitfield is in left, and the bitfield also gets extracted to left. The insertion instruction takes the value to insert in left, and the address to insert to in right (like the pop-store instruction without the pop).


Last edited by AWJ on Tue May 16, 2017 5:42 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Koei bytecode
PostPosted: Tue May 16, 2017 5:09 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5832
Location: Canada
This is really neat! Thanks for documenting it.


Top
 Profile  
 
 Post subject: Re: Koei bytecode
PostPosted: Tue May 16, 2017 5:25 pm 
Offline

Joined: Mon Nov 10, 2008 3:09 pm
Posts: 431
32 BIT INSTRUCTION SET
Code:
code  | nickname       | args  | operation
------+----------------+-------+-------------------------------------
 B700 | (illegal)      |  ---  | (illegal)
 B701 | LONG MULT      |   0   | left = left * right
 B702 | LONG SDIV      |   0   | left = (int32)left / (int32)right
 B703 | LONG ADD       |   0   | left = left + right
 B704 | LONG SUB       |   0   | left = left - right
 B705 | LONG MINUS     |   0   | left = -left
 B706 | LONG CMPEQ     |   0   | left = (left == right)
 B707 | LONG CMPNE     |   0   | left = (left != right)
 B708 | LONG SCMPLT    |   0   | left = ((int32)left < (int32)right)
 B709 | LONG SCMPLE    |   0   | left = ((int32)left <= (int32)right)
 B70A | LONG SCMPGT    |   0   | left = ((int32)left > (int32)right)
 B70B | LONG SCMPGE    |   0   | left = ((int32)left >= (int32)right)
 B70C | LONG LOADL far |   2   | left = <far>
 B70D | LONG LOADR far |   2   | right = <far>
 B70E | LONG STORE far |   2   | <far> = left
 B70F | LONG PUSH far  |   2   | push <far>
------+----------------+-------+-------------------------------------
code  | nickname       | args  | operation
------+----------------+-------+-------------------------------------
 B710 | LONG LOADL abs |   2   | left = <abs>
 B711 | LONG LOADR abs |   2   | right = <abs>
 B712 | LONG STORE abs |   2   | <abs> = left
 B713 | LONG PUSH abs  |   2   | push <abs>
 B714 | LONG PUSHL     |   0   | push left
 B715 | LONG POPR      |   0   | pop right
 B716 | LONG DEREF     |   0   | left = *(int32 *)left
 B717 | LONG POPSTORE  |   0   | pop right; *(int32 *)right = left
 B718 | LONG LOADL imm4|   4   | left = <imm4>
 B719 | LONG LOADR imm4|   4   | right = <imm4>
 B71A | LONG SWAP      |   0   | swap left <-> right
 B71B | LONG INC       |   0   | ++left
 B71C | LONG DEC       |   0   | --left
 B71D | LONG BOOL      |   0   | left = ((int32)left != 0)
 B71E | LONG SMOD      |   0   | left = (int32)left % (int32)right
 B71F | LONG LSHIFT    |   0   | left = left << right
------+----------------+-------+-------------------------------------
code  | nickname       | args  | operation
------+----------------+-------+-------------------------------------
 B720 | LONG SRSHIFT   |   0   | left = (int32)left >> right
 B721 | LONG COMPL     |   0   | left = ~left
 B722 | LONG AND       |   0   | left = left & right
 B723 | LONG OR        |   0   | left = left | right
 B724 | LONG XOR       |   0   | left = left ^ right
 B725 | LONG SEXTEND   |   0   | left = (int32)(int16)left
 B726 | LONG UEXTEND   |   0   | left = (uint32)(uint16)left
 B727 | LONG NOP       |   0   | no operation
 B728 | LONG NOT (?)   |   0   | apparently bugged (see below)
 B729 | LONG UCMPLT    |   0   | left = ((uint32)left < (uint32)right)
 B72A | LONG UCMPLE    |   0   | left = ((uint32)left <= (uint32)right)
 B72B | LONG UCMPGT    |   0   | left = ((uint32)left > (uint32)right)
 B72C | LONG UCMPGE    |   0   | left = ((uint32)left >= (uint32)right)
 B72D | LONG URSHIFT   |   0   | left = (uint32)left >> right
 B72E | LONG UDIV      |   0   | left = (uint32)left / (uint32)right
 B72F | LONG UMOD      |   0   | left = (uint32)left % (uint32)right

Extension and bool conversion
16-bit instructions only use the lower halves of the registers and leave stale data in the upper halves, so it is necessary to sign-extend or zero-extend when doing arithmetic on mixtures of 16-bit and 32-bit data. Conversely, the bool conversion instruction is needed because the conditional jump instructions only check the (non-)zeroness of the lower half of left.

Instruction $B728
Instruction $B728 is probably supposed to be the long version of logical NOT ($CA) but actually has the same effect as instruction $B71D. None of the games I have inspected uses this instruction (the binary sequence $B7 $28 is nowhere to be found in the ROMs) so if it is a bug it seemingly went undetected for as long as Koei used this interpreter.


Last edited by AWJ on Tue May 16, 2017 7:58 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Koei bytecode
PostPosted: Tue May 16, 2017 7:44 pm 
Offline

Joined: Wed Jul 31, 2013 10:45 am
Posts: 3
This is really cool and I think it definitely at least deserves a page on the NESdev wiki for posterity. Thanks for documenting it.


Top
 Profile  
 
 Post subject: Re: Koei bytecode
PostPosted: Tue May 16, 2017 8:29 pm 
Offline

Joined: Mon Sep 27, 2004 2:57 pm
Posts: 1248
Wiki page seconded, it'll be easier to organize and maintain.


Top
 Profile  
 
 Post subject: Re: Koei bytecode
PostPosted: Wed May 17, 2017 12:41 am 
Offline
User avatar

Joined: Sat Jul 12, 2014 3:04 pm
Posts: 950
Neat!

It seems more appropriate for Datacrystal or Zophar as to where to document, but yes to archival.


Top
 Profile  
 
 Post subject: Re: Koei bytecode
PostPosted: Wed May 17, 2017 3:16 am 
Offline
User avatar

Joined: Wed Oct 16, 2013 7:55 am
Posts: 130
This is so awesome! Thanks so much for doing all the hard work.

Looking forward to working on a disassembler or decompiler, unless someone else gets around to it first.


Top
 Profile  
 
 Post subject: Re: Koei bytecode
PostPosted: Wed May 24, 2017 5:51 pm 
Offline
User avatar

Joined: Thu Jan 03, 2008 1:48 pm
Posts: 543
I guess someone should write an open source interpreter too. :X


Top
 Profile  
 
 Post subject: Re: Koei bytecode
PostPosted: Wed May 24, 2017 6:36 pm 
Offline
User avatar

Joined: Sat Jul 12, 2014 3:04 pm
Posts: 950
^^^ I guess you just volunteered?
Quote:
Bitfields
There are three bitfield instructions: one to extract (load from) a signed bitfield, one to extract an unsigned bitfield, and one to insert (store to) a bitfield. For all three bitfield instructions, the first byte after the instruction is the size of the bitfield in bits, and the following byte is the bit position counting from the LSB. The entire bitfield must fit into a 16-bit word (i.e. size + pos < 16)
I was going to ask if the fields were little- or big-endian, but I suppose unless you're trying to make machines interoperable with the states of each other (hardly necessary) it doesn't matter.

Quote:
CA | NOT | 0 | left = !left
CB | MINUS | 0 | left = -left
CC | COMPL | 0 | left = ~left
with all three existing, I'm curious what NOT does. Is it LEFT[15:0] = (LEFT[15:0] ==0 ? 1 : 0)?

ed: fixed to only write 16 bits to answer.


Last edited by Myask on Tue May 30, 2017 7:32 pm, edited 2 times in total.

Top
 Profile  
 
 Post subject: Re: Koei bytecode
PostPosted: Wed May 24, 2017 6:44 pm 
Offline

Joined: Mon Nov 10, 2008 3:09 pm
Posts: 431
Myask wrote:
^^^ I guess you just volunteered?
Quote:
Bitfields
There are three bitfield instructions: one to extract (load from) a signed bitfield, one to extract an unsigned bitfield, and one to insert (store to) a bitfield. For all three bitfield instructions, the first byte after the instruction is the size of the bitfield in bits, and the following byte is the bit position counting from the LSB. The entire bitfield must fit into a 16-bit word (i.e. size + pos < 16)
I was going to ask if the fields were little- or big-endian, but I suppose unless you're trying to make machines interoperable with the states of each other (hardly necessary) it doesn't matter.


Little-endian. That's what I meant by "counting from the LSB". A bitfield with a position of 0 is aligned with the LSB of the word it's in, so no shifting is needed on insertion or extraction.


Top
 Profile  
 
 Post subject: Re: Koei bytecode
PostPosted: Sat May 27, 2017 6:09 pm 
Offline

Joined: Mon Nov 10, 2008 3:09 pm
Posts: 431
Myask wrote:
Quote:
CA | NOT | 0 | left = !left
CB | MINUS | 0 | left = -left
CC | COMPL | 0 | left = ~left
with all three existing, I'm curious what NOT does. Is it LEFT = (LEFT[15:0] ==0 ? 1 : 0)?


Yes.


Top
 Profile  
 
 Post subject: Re: Koei bytecode
PostPosted: Mon May 29, 2017 3:18 pm 
Offline

Joined: Wed May 19, 2010 6:12 pm
Posts: 2366
I've been thinking about something similar for the 65816, but with the "word-code" being a list of addresses to jump to.


Top
 Profile  
 
 Post subject: Re: Koei bytecode
PostPosted: Wed May 31, 2017 3:02 am 
Offline

Joined: Tue Feb 07, 2017 2:03 am
Posts: 256
Why not look at and extract the 16bit KOEI titles to see if they already have the above in 16bit?


Top
 Profile  
 
 Post subject: Re: Koei bytecode
PostPosted: Wed May 31, 2017 12:24 pm 
Offline

Joined: Wed Nov 30, 2016 4:45 pm
Posts: 93
Location: Southern California
psycopathicteen wrote:
I've been thinking about something similar for the 65816, but with the "word-code" being a list of addresses to jump to.

That of course is what Forth normally does, at least in the indirect-threaded code (ITC) and direct-threaded code (DTC) models. On the 6502.org forum, Bruce Clark presents the idea of using the 16-bit S (stack-pointer) register as the instruction pointer in DTC Forth on the '816, at http://forum.6502.org/viewtopic.php?t=586 . Basically the program itself is on the hardware stack! The earlier idea of a two-instruction NEXT in ITC Forth on the '816, which he mentions at the top of the head post, is discussed at http://forum.6502.org/viewtopic.php?t=584 . Just when you get all comfy with some method, along comes Bruce with some wild idea to turn everything upside down and get you to consider a very different approach that may be a lot better in some situations.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources


Top
 Profile  
 
 Post subject: Re: Koei bytecode
PostPosted: Wed May 31, 2017 8:48 pm 
Offline

Joined: Wed May 19, 2010 6:12 pm
Posts: 2366
If you use the stack, you can easily do long addressing.

plx
plb
inc $0000,x
rts


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 43 posts ]  Go to page Previous  1, 2, 3  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: TmEE and 10 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