找回密码
 注册
搜索

[L2] [T-25143]ラングリッサー2魔族編开发讨论专贴

[复制链接]
发表于 2009-6-19 00:10 | 显示全部楼层
老实说,还是不太明白为什么汉化版的无法更改其中的道具说明文字和数字
 楼主| 发表于 2009-6-19 08:24 | 显示全部楼层
主要是因爲ROM内字庫不同而造成的。
而我只對原版的字庫進行的100%的解析,並最終做出了編輯工具,所以可以對原版文字進行編輯。
而我對汗化版的字庫進行的的解析只有0.1%而已,所以.....

原版商店文字:


漢化版商店文字:


原版字庫:


汗化版字庫:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 发表于 2009-6-19 08:36 | 显示全部楼层
最終用來表示給玩家的任何文字介紹都是通過引用字庫指定文字從而實現的。

好比說,簡體字"艾尔文",原版字庫中根本不存在 "艾“與”尔",所以必須對字庫進行增加,但是字庫的縂容量是有限的,把所有沒有的簡體字都加進去是沒有空間的,漢化者只能通過替換原版中那些在以後中文版中用不上的日文與漢字,試最終字庫中已經不存在日文與漢字,取而代之的是簡體字。

這些簡體字對應的編碼自然也就河原版不同了,再以原班的角度進行修改自然是不可能對應文字。


不過道具説明方面,剛才發現,貌似數字方面還有對應,可以把商品介紹數字修改為我前面採用的那種?的形式或你說的護身符+15改爲 +1+ 或+1~  或+1?
甚至我那種全體的+??
 楼主| 发表于 2009-6-19 11:31 | 显示全部楼层
那莫再繼續放一部分關於M68000彙編機械語言的極其重要的資料:
ASM Guide:  by hacking-cult.org   drx

1.
Research / Megadrive Programming / Basics
Genesis has one CPU called 68000 (68k for short). It understands a set
of 16-bit instructions, which are called machine code (like 4E75, 31FC
etc.). But programming in hexes isn't very easy (well, it's not
impossible... few years ago I programmed only using machine code =P),
so nifty programmers made the assembly language. There is a program
(assembler) which assembles this to machine code. Assembly is a bit
more humanized. It's not BASIC, but it's not hard. I will try to make
you understand this. I can't guarantee that it will be interesting =P
Genesis has 64kb of RAM. You can store everything there. There is a VDP
(graphics CPU) which controls graphics =P It has 64k of RAM (called
VRAM - Video RAM), which can store patterns, sprites atributes, scroll
data etc.
Here's a map of 68k memory:
$000000-$3FFFFF - ROM
$400000-$9FFFFF - Unused
$A00000-$A0FFFF - Z80
$A10000-$A1001F - I/O
$A10020-$BFFFFF - Miscellaneous
$C00000-$C0001F - VDP
$C00020-$DFFFFF - VDP mirrors
$E00000-$FFFFFF - RAM (it repeats each $10000, so $E00000 is the same as $E20000 and $FF0000. Most games use $FF0000-$FFFFFF)
There are three data types. Byte, word (2 bytes) and longword (4
bytes). There is no unsigned or signed types like in C, because each
data type can be treated as unsigned or signed. There are 16 registers
which store temporary data (kinda like variables). Each one is a
longword. There's 8 data registers called D0-D7 and 8 address registers
(A0-A7). You should use A0 to A6, because A7 is the stack pointer (I'll
tell you about it later). Hey, but what's the difference between
address and data registers? Data registers store data, and address
registers usually store pointers to some location in RAM. For example, address registers can be incremented or decremented.
68000 stores data in Big Endian way. So longword $12345678 is stored
$12,$34,$56,$78 in memory, compared to Little Endian $78,$56,$34,$12.
x86 and Z80 use Little Endian. Z80 is for the music stuff... (x86 is
the CPU in your PC)
Don't worry if you don't get something or you forget all this stuff. You will get used to it.
 楼主| 发表于 2009-6-19 11:33 | 显示全部楼层
2.
Research / Megadrive Programming / Let's start
Ok, you need some stuff first. You have to download SNASM68k from our downloads section and a Sonic 1 disassembly
(I suggest you download my disasm because I will refer to its labels).
Now open the Sonic 1 disassembly in notepad. Many lines isn't there? =P
Comments are marked by ; or *. The game code starts with label 'Entrypoint:'.
Ok, let's start with the most interesting part of programming, programming XD. Ok, first command, MOVE.
This command moves some data (constants, registers, RAM) into either
another register or RAM location. JohnnyUK nicely called it 'copying'.
Syntax: move source,destination
Exapmles:
        move.w        #2,($FFFFFE10).w
        move        (a6),d3
        move.b        ($FFFFFE12).w,(a5)+
Looks complicated? Don't worry, I'll explain everything =) First, why .w, .b? If you use it, you will specify the data length being copied. If you don't the assembler will probably choose .l. .b is byte, .w is word and .l - longword.
For example:
move.b        #1,($FFFFFE10).w
before - FFFFFE10: 05 06 07 08
after  - FFFFFE10: 01 06 07 08
move.w        #1,($FFFFFE10).w
before - FFFFFE10: 05 06 07 08
after  - FFFFFE10: 00 01 07 08
move.l        #1,($FFFFFE10).w
before - FFFFFE10: 05 06 07 08
after  - FFFFFE10: 00 00 00 01
I hope you get it =P Now, #x is a immediate value (a number, constant etc =P). Examples:
#5 - decimal 5
#$20 (hex) - decimal 32
#%1001 (binary) - decimal 9
#@777 (octal) - decimal 511
#'SEGA' - ascii text (remember, this can be tricky. 1 letter - use .b, 2 letters - use .w, 4 letters - use .l)
($FFFFFE10).w is an absolute address. Locations in ram HAVE TO be .w. ROM addresses usually are .l. Some programmers (forgot the name of the game...) use .w
for short ROM addresses, but that can cause serious bugs. Remember,
never write anything to a ROM address ;) (real Genesis will crash)
Examples:
move.b        #1,($FFFFFE12).w
moves 1 (byte) to $FFFFFE12 in RAM (lives in Sonic 1)
move.l        ($12346).l,d0
moves a longword from address $12346 in ROM to data register d0.
move.w        (a0)+,d0
moves a word from the location at A0 to d0, increments a0 by 2 (length of word) after that.
I'll explain the registers now (already told about it before, but let's
revise XD). There are 16 registers - 8 data registers and 8 address
registers. All registers are longwords. Data registers are called d0-d7 and can store any data. Address registers usually store pointers to some location in ROM or RAM, but they can also be used for data. Address registers are a0-a7, but don't use a7 because it's the stack pointer (I'll explain later). Registers are a nice way of storing temporary data. Eg.
move.b        ($FFFFFE12).w,d0 ;copy value at $FFFFFE12 to d0 (this is a comment =P)
move.b        d0,($FFFFFE13).w ;move d0 to $FFFFFE13
Of course this could be done with:
move.b        ($FFFFFE12).w,($FFFFFE13).w
but this is an example =P The biggest reason why use registers is that you can modify them without modifying the source, eg.
move.b        ($FFFFFE12),d0        ;copy $FFFFFE12 to d0
add.b        #2,d0                ;add 2 to d0
;do something with d0
We have a new command, ADD. It adds a number to a register or a place in ram.
Syntax: add source,destination
Examples:        add.b        #2,($FFFFFE12).w        ;increment $FFFFFE12 by 2
                add.l        (a0),a0                        ;increment a0 by value at a0
                add        d0,d0                        ;add d0 to d0 (d0 = d0 * 2)
What is the difference between a0 and (a0)?
a0 is the address, only a number. And (a0) is the value at this address. Have a look at this:
$FFFFFE12: 01        ;some value in ram
a0: $FFFFFE12        ;address
(a0): 01        ;value at address $FFFFFE12
You can postincrement or predecreament address registers:
;a0 is incremented by 1 (length of byte) after the instruction
                move.b        (a0)+,d0
;a0 is decremented by 2 (length of word) before the instruction
                move.w        -(a0),d0
Exapmle:
move.l        (a0)+,d0
before: a0: $00012345
        d0: $00000000
$000012345: $FFFFFFFF
after:  a0: $00012349
        d0: $FFFFFFFF
$000012345: $FFFFFFFF
Hope you understand =)
Now, the opposite of adding is substracting. You do it using SUB instruction.
Exapmles:
                sub        #5,a0
                sub.w        (a0),($FFFFFE12).w
                sub.b        d0,d1
That would be the end of this chapter. In next lesson we'll learn about LEA,
labels, comparing and branching depending on the results (like
if...else). It's about 3 am and I'm a bit tired. If you don't
understand something or you have any questions, ask on the forums =)
 楼主| 发表于 2009-6-19 11:36 | 显示全部楼层
3.
Research / Megadrive Programming / Compares, branches...
Yay, finally got some time to write another lesson (it's almost 12pm).
Anyway, we'll learn about compares, branches and subroutines. But
first, here's some additional info to the previous lesson:
There are few types of MOVE. There's MOVEA, MOVEM, MOVEP and MOVEQ. Usually the compiler chooses the right one, but you sometimes have to choose the correct one (assemblers aren't very smart ;).
MOVEA moves an address into an address register. The syntax:
        movea        address,a0-a7
As I said before, don't use a7. Just wait =) The address is an effective address (can be a register, immediate value etc.) Here are some examples:
        movea    #$12346,a0     ;moves address $12346 to a0
        movea   -(a1),a5        ;moves address at value at pointer
                                ;in a1 to a5, decrements a1 before instruction
        movea   d0,a3           ;moves d0 to a3
I'll introduce the labels now :) Remember BASIC and goto function? Labels are similiar:
        somecode
        bra        Label
        somecode
Label:
        somecode
BRA goes (jumps) to a label. Bcc instructions can branch on certain conditions, but they can be made with the CMP instruction. This means a bunch of new instructions ;) Here's the example code:
        game code
        game code
        bsr        LifesSub
        game code
        game code
LifesSub:
        cmp.w        #$0102,($FFFFFE10).w
        bne        TheLevelIsNot0102
        add.b        #1,($FFFFFE12).w
TheLevelIsNot0102:
        rts
Oh my, so many new things. We created label 'LifesSub'. It's called by BSR, what's that? BSR means Branch to SubRoutine.
Subroutines are like functions in C (or other programming languages).
You can call them whenever you want. There is one important thing
though, after the end of subroutine you have to put rts, which returns to the state before subroutine calling. Example:
        bsr        Foo
        bsr        Foo
        bsr        Foo
        ...
Foo:
        code
        rts
Now, when Genny sees BSR (8 or 16 bit instruction, relative address) or JSR (32 bit absolute address, I'll tell about it later), it copies the next insruction PC
(Program Counter - register which stores info about the current
instruction location in ROM, like where the code is now, it's hard to
explain ;) state to stack.
I promised before that I'll tell you what the hell stack is. Stack is a reserved section in RAM (CAN be in SRAM, but it isn't tested yet ;) for temporary things, like our PC state. Now you see why RTS is important. RTS copies the PC state back from stack.
So, what's the CMP thingy? It COMPARES two values. It's not exactly if...else statement like in C. BNE means 'Branch if Not Equal'. So if $FFFFFE10 (level) isn't $0102 (these two values are the same, equal), it branches. If it isn't, it doesn't. Simple, huh? There are many Bcc instructions, one of them being BEQ (Branch if EQual) which works opposite to BNE. I mentioned JSR some lines before. It is like BSR,
but it doesn't branch, but jumps to a subroutine. What's the
difference? Branch instruction is small (1 byte if branch is between
-127...127 and 2 bytes if it's between -32 768...32 768) but it's
limited. Some games like Sonic 3 & Knuckles use RAM for... code.
Yeah, small parts of code that can be edited at any time. You can't
branch to RAM, that's first. Second, the limitation. $8000 back or
forth is not very big. There's the JSR. The instruction is longer (4 or
6 bytes, depending on the type), but it's an absolute address (you can
make a relative jsr insruction, but I've already told enough
new things today...), so you can jump to any address in 68000 memory.
For 68k memory map, see additional info. Examples:
        jsr        $FFFFFFF0
        jsr        Label
        jsr        a0
The absolute equivalent of BRA is JMP, which jumps to an absolute address without puting the PC on the stack.
The last two things we're gonna learn today are MOVEQ and MOVEM.
MOVEQ is similar to MOVE, but it means MOVE Quick. It's a faster (and smaller - 1 byte) instruction. You can only move 1 byte. Example:
        moveq        #0,d0
Clears ENTIRE d0, because when you moveq into a register, it sets the ENTIRE register.
It clears the register! Move.l would take 6 bytes (hey, in old times space was worth more than gold), when moveq only 1.
You can also use ADDQ and SUBQ (they can also change values in ram)
Now, the last thing. MOVEM (MOVE Multiple).
It can copy more than one register to a location in RAM or in reverse
(RAM to more than one register). As I said before A7 is the stack
pointer. So you can use stack, too! The reason why MOVEM was
created are subroutines. Subroutines are called in many different
places in ROM. And subroutines often use registers. Now if you don't
want to change some important registers inside a subroutine, you should
backup these registers before the subroutine and copy them back after.
Don't get it? Look at the example:
        move.b        #$10,d0
        bsr        Subroutine
        move.b        d0,d1
        ...
Subroutine:
        ...
        moveq        #0,d0
        ...
        rts
Take a look at that code. We set d0 to $10, then the subroutine clears d0. After the subroutine, we move d0 to d1. That's not exactly what we wanted (it'll set d1 to 0, we wanted it to be $10). To fix that we can use movem:
        move.b        #$10,d0
        bsr        MovemSub
        move.b        d0,d1
        ...
MovemSub:
        movem        d0-d2,-(sp)
        ...
        moveq        #0,d0
        ...
        movem        (sp)+,d0-d2
        rts
Now it'll work! the SP is the stack pointer. You could use A7 (it's the same), but I use sp. We decremented sp, because if we didn't, we would change some important data in SP. and after that, we increment SP back to its state. The register list can be different. Eg.
d0-d5 - d0, d1, d2, d3, d4, d5
a5-a6 - a5, a6
d1-d3/d6-d7 - d1, d2, d3, d6, d7
d6-a1/a5-a6 - d6, d7, a0, a1, a5, a6
There are many different possibilites... If you don't understand MOVEM,
just skip it, and come back to it when you will know more about ASM. It
took me 4 tries to understand that thing XD (and I didn't have any
nifty guides *shot*)
That's all folks! In next lesson I'll introduce some more Bcc instructions and maybe some bit operations? G'night.
 楼主| 发表于 2009-6-19 11:39 | 显示全部楼层
4.
Research / Megadrive Programming / Logical operations
Before we start with logical operations, I will show some more Bcc
instructions as I promised. Apart from data registers, address
registers and program counter, there's an another register (last one, I
promise) called SR (status register). We are now only interested in 5 bits of it, named CCR (flag register). Everything moved to CCR will be copied in the lower byte of SR (this works in reverse). Anyway, CCR
consists of 5 bits, which are flags. These flags are set or cleared
when certain thing is being done with registers, or an instruction is
called. These flags are:
- C Flag (carry) - 9th bit of adding, shifting and bit rotation.
- V Flag (overflow) - It'll be set when result can't be represented. Eg. when you add $01 (byte) to $7F (byte), the result is $80 and V flag will be set
- Z Flag (zero) - Will be set when the result is zero.
- N Flag (negative) - Will be set if the highest bit of result is set (in two complement it's the sign bit, so the signed negative)
- X Flag (extended) - A copy of C flag.
But what these things have to do with CMP and Bcc? In C, this code:
something == 1
will return 1 if it's true. Now, when you compare two values, Z flag is set when they are the same or is cleared when they're different. BEQ branches if Z flag is set and BNE will branch if Z flag is cleared. So... BEQ and BNE can be used not only when comparing two values. Look at that example:
        moveq        #0,d0
        beq        zflagisset
        rts
zflagisset:
It will branch, because the result of setting d0 to 0 is 0 and Z flag is set. And BEQ branches if Z flag is set. This comes in handy if you want to test if a number is null or not. Here's a list of Bcc instructions:
instr        conditions                                flags required
BEQ        Branch if equal                                Z = 1
BNE        Branch if not equal                        Z = 0
BGE        Branch if greater or equal (signed)                N = V
BGT        Branch if greater than (signed)                N = V, Z=0
BLT        Branch if lesser than (signed)                N != V, Z=0
BLE        Branch if lesser or equal (signed)                N != V
BHI        Branch if higher than (unsigned)                C = 0, Z = 0
BHS        Branch if higher or same as (unsigned)        C = 0 OR Z = 1
BLO        Branch if lower than (unsigned)                C = 1, Z = 0
BLS        Branch if lower or same as                        C = 1 OR Z = 1
BCC        Branch if carry clear                        C = 0
BCS        Branch if carry set                        C = 1
BVC        Branch if overflow clear                        V = 0
BVS        Branch if overflow clear                        V = 1
BPL        Branch if plus                                N = 0
BMI        Branch if minus                                N = 1
BRA        Branch Always
= means equal, != means not equal =P
Enough of these branches, let's learn about bits...
I really hope you know what bits and bytes are. If you don't, a byte
consists of 8 bits, which can be either 1 (set) or 0 (cleared). Number
7 is 111 binary. You can do fun stuff with bits. Eg. the keys pressed
on joypad are all stored in one byte. Each bit of this byte represents
a key:
7 6 5 4 3 2 1 0
S A C B R L D U
The lowest bit is up, bit 1 is down etc...
Now if you want to know if B is pressed, what should you do? You should use the AND instruction.
AND operation compares all bits of two values and sets a bit in
a new one if this bit was set in both of the values. I'm not an expert
of logical operations so you can laugh =P Example:
Value 1: 10101100
Value 2: 11001010
         --------
Result:  10001000
Take a look at this code:
        move.b        ($FFFFF604).w,d0        ;$FFFFF604 is the joypad output in Sonic 1
        and.b        #$10,d0                        ;you could use #%00010000
        beq        Bnotpressed
Imagine we press B, Up and Start. And instruction will look like that:
         SACBRLDU
Value 1: 10010001 (keys pressed)
Value 2: 00010000 (and)
         --------
Result:  00010000
The result is not zero so it won't branch! Nifty, isn't it? There are other:
        or.b        #$8,d0
OR sets the result bit if value 1 bit OR value 2 bit is set.
Value 1: 00100100
Value 2: 00001000
         --------
Result:  00101100
Next:
        eor.b        #$24,d0
EOR sets the result bit if one and ONLY one bit is set in value bits (like x86 xor).
Value 1: 00101110
Value 2: 00010100
         --------
Result:  00111010
And next:
        not.b        d0
NOT clears the bit if it was set and sets the bit if it was cleared. (changes all bits)
Before:  01001001
After:   10110110
And now on with bit shifting. This operation moves bits by an ammount
to the right or to the left. Eg. we'll shift a value 3 times to the
right:
Before:  00101000
After:   >>>00101000
So after this it'll be:
         00000101
Bit shifting is fun because one shift to the left multiples a number by
2 and one shift to the right divides it by 2. The instructions:
        lsl.b        #3,d0
        lsr.b        #2,d1
LSL shifts x bits to the left, LSR shifts x bits to the right. There's also bit rotating:
        rol.b        #1,d0
It'll rotate a number one bit to the left:
Before:  10101010
After:  10101010
        |-------^
         01010101
It works the same to the right. The last part of bits:
        bset        #1,d0        ;sets bit 1 of d0
        bclr        #2,d1        ;clears bit 2 of d1
        bchg        #3,d2        ;changes bit 3 of d2 (like not)
        btst        #6,d0        ;test if bit 6 of d0 is set, sets
                        ;z flag if it's cleared else clears z flag
homework:
        move.b        #$4,d0
        or.b        #$1,d0
        eor.b        #$2,d0
        lsl.b        #1,d0
        ror.b        #2,d0
        btst        #7,d0
        bne        branch
        rts
branch:
Now your homework is to guess if it is going to branch or not. Bit operations are boring at first but you'll get used to them...
 楼主| 发表于 2009-6-19 11:43 | 显示全部楼层
5.
Research / Megadrive Programming / Editing the game code
In this chapter we'll learn how to search for wanted code and edit it.
But before that, I'll show some arithmetic and data functions.
First, the LEA (Load Effective Address) instruction. It loads an effective address into an address register. You should use this instead of MOVEA. Examples:
        lea        ($FFFFFE10).w,a1
        lea        ($12345).l,a2
        lea        (Label).l,a3
        lea        4(a0),a4
        lea        4(pc),a5
lea 4(a0),a4 loads the value at a0 + 4 into a4. lea 4(pc),a5 loads PC + 4 into a5.
You can multiply and divide in 68k (which I didn't know for years, really XD). There are 4 instructions for it: DIVS (DIVide Signed), DIVU (DIVide Unsigned), MULS (MULtiply Signed), MULU (MULtiply unsigned). Examples:
        divu        #5,d0        ;divide d0 value by 5 (unsigned)
        divs        d1,d2        ;divide d2 value by d1 value (signed)
        mulu        #3,d3        ;multiply d3 value by 3 (unsigned)
        muls        d3,d0        ;multiply d0 value by d3 value (signed)
As you know or not, unsigned $FE is $FE and signed $FE is -$2. There's a difference between multiplying by $FE and -$2:
        moveq        #4,d0
        move.l        d0,d1
        mulu        #-$2,d0        ; d0 = $4 * $FE, d0 = $3F8
        muls        #-$2,d1        ; d1 = $4 * -$2, d1 = -$8 ($F8)
As you can see there is a difference. The last three instructions are different from the ones you have seen already. NOP does nothing. It really doesn't XD. It is used to make VERY small delays, to let hardware parts to act. Example (it doesn't do anything):
        move.w        #4,d0
        nop                ;it really does nothing =P
        moveq        #0,d0
The second instruction is ILLEGAL.
It's not a real instruction, it's just a random word (depending on the
assembler, normally TAS.B something) that will cause an illegal
instruction interrupt (interrupts will be covered later).
And the last one, DC. This instruction is to store data in ROM. Can be used as DC.B, DC.W and DC.L. I'll better show an example:
        dc.b        $12,$24,$31,$21,$21,$31,$31,$12
;this will store $1224312121313112 in ROM.
        dc.w        $0EEE, $0CCC, $0AAA, $0888, $0666, $0444, $0222, $0000
;a random palette ;)
        dc.l        $1,$123141,$14144144
        dc.w        %1110,1000
The possibilities are endless. Here's our first real example:
        lea        (Palette_Sega).l,a0
        lea        ($FFFFFA80).w,a1
        move.w        (a0)+,(a1)+
        rts
Palette_Sega:        dc.w  $EEE, $EEA, $EE4,        $EC0
                dc.w  $EE4, $EEA, $EEC,        $EEA
                dc.w  $EEA, $EEA, $EEA,        $EEA
                dc.w  $EEC, $EEA, $EE4,        $EC0
                dc.w  $EC0, $EC0, $EEC,        $EEA
                dc.w  $EE4, $EC0, $EA0,        $E60
                dc.w  $EEA, $EE4, $EC0,        $EA0
                dc.w  $E80, $E00
It loads the palette address to a0 and the palette address in ram to a1. Copies first color/colour (a0) to the first color in ram, then increments a0 and a1. This really works =)
Now what I promised - editing the code in the game. Open the Sonic 1 disassembly. Search it for label 'PlayLevel:'. There's a bunch of variable settings (a list of RAM variables is in Guides>Sonic 1>RAM).
I commented some stuff there. The thing we are interested in is editing
the lives count. The RAM location that stores lifes is $FFFFFE12. Change it to 4.
Now, how do we assemble our edited code? I made a batch program which
assembles a code using SNASM68K. Call it 'asm.bat' or something and put
this in there:
@echo off
echo asm.bat by drx
if "%1"=="" goto noinput
snasm68k.exe -emax 0 -p -o ae- %1.asm, %1.bin
goto end
:noinput
set /p input=  Input asm file   
set /p output= Output bin file   
snasm68k.exe -emax 0 -p -o ae- %input%, %output%
:end
There are two options of using it:
1) Open it normally, type the input asm file and the output bin file
and it will assemble it
2) Go to the command line, type 'asm FILE' (without the quotes) and
it'll assemble FILE.asm to FILE.bin (it'll show the errors and the
window won't disappear in Win XP...)
Now open the assembled file in an emulator. If you made it correctly, you should have 4 lives.
Few things that are different in disasm from what I said before:
1) Bcc.s or Bcc.w - this is wrong. You shouldn't use this, assembler will choose the correct one. .s is a one byte branch (-127..127) and .w is a two bytes branch (-32767..32767).
2) There are instructions like CMPI, CMPA, ADDA, ADDI etc. That are just different types of the same instruction, you should just use CMP or ADD and the assembler will choose the correct one. The exception is MOVEM, MOVEQ, ADDQ, xQ etc.
If you understand 90% of what has been written here, then it's good.
Hell, if you understand 20% of it, it's good XD. Assembler is not easy,
and a smallest error can ruin all your work.
"50% of the time spent programming an assembly language is correcting bugs." - an assembly programmer XD
The more time you spend on it, the better you are at fixing those bugs XD
The most common bugs:
1) You MUST put a # before an immediate value (move.b #1,d0) If you won't, this will be treated as an address.
2) When you move a longword into d0, then a byte, the upper bytes still are there! To clear d0 use moveq #0,d0 or clr.l d0
3) This is the most annoying bug ever. PC must be ALWAYS
even. if you insert one byte somewhere, the code after it will be all
in odd addresses and the Genesis will crash. Also, try to load even ROM
addresses into address registers, because that can be nasty, too. The
best method to make sure an address will be always even is to put this
before it:
        align $2
or this:
        even
This will make it always even. But remember, don't put it between the
code, or it'll give you an illegal operation (it SHOULDN'T, because the
PC is always even but...)
You can align as many bytes as you want, eg putting this at the end will make the output rom size be a multiplication of $80000:
        align $80000
With the knowledge you have (I hope you do) you should be able to find
and edit the basic things like lives. Remember, the best method to
learn is to discover everything on your own. Don't rely only on my
guides, be creative =)
Next chapter will cover loops and arrays (not C-like arrays...). Have fun.
 楼主| 发表于 2009-6-19 11:46 | 显示全部楼层
6.
Research / Megadrive Programming / Loops & arrays
Loops in 68k are very limited... Forget about while- or for-like loops ;) For loops, you use DBcc. Usually, programmers just use DBF or DBRA
(which mean the same). First operand is a data register that will be
decremented and the second one is a label where to loop if this data
register isn't -1. Example:
        lea        ($1234).l,a0
        lea        ($FFFF1234).w,a1
        moveq        #7,d0
Loop:
        move.w        (a0)+,(a1)+
        dbf        d0,Loop
This example will go back to the Loop label 8 times (d0 + 1). It's not that hard... I won't show the other DBcc instructions because they are practically useless. Maybe later, when I'll write an instruction list...
Second part of this chapter will be arrays. Well, I call them arrays,
but they really aren't. Those are just sets of bytes, words, longwords
that are readed depending on some variable. For instance, we want to
load a different music depending on the current level:
        moveq        #0,d0                        ;clear d0
        move.b        ($FFFFFE10).w,d0        ;move level to d0 (00 - GHZ, 01 - LZ etc)
        lea        (MusicArray).l,a0        ;load the music values array into a0
        move.b        (a0,d0.w),d0                ;see the explanation
        rts
MusicArray:
        dc.b        $81,$82,$83,$84,$85,$86
I thought 5 damn minutes about how should I explain this XD. The move.b (a0,d0.w),d0 instruction moves a byte at a0 + d0 to d0. Let's examine 3 possibilities (GHZ - 00, LZ - 01, MZ - 02)
GHZ:
        d0 = 00
        a0 = $1234
move.b        (a0,d0.w),d0
        d0 = (a0 + d0)
        d0 = ($1234 + 0)
        d0 = ($1234)
        d0 = $81
LZ:
        d0 = 01
        a0 = $1234
move.b        (a0,d0.w),d0
        d0 = (a0 + d0)
        d0 = ($1234 + 01)
        d0 = ($1235)
        d0 = $82
MZ:
        d0 = 02
        a0 = $1234
move.b        (a0,d0.w),d0
        d0 = (a0 + d0)
        d0 = ($1234 + 02)
        d0 = ($1236)
        d0 = $83
I hope you get it... This chapter was short but very important, so if
you don't understand something, read it 10 times =P And you can always
ask on the forums.
====================================
以上這些如果能理解的話,遊戲的高級自定義與設計基本就沒問題了............可以按自己的意思去編譯遊戲。
发表于 2009-6-19 13:30 | 显示全部楼层
既然可以改成+1+ 或+1~ 或+1? 甚至我那種全體的+?? 那么能不能改成具体的数字呢,感觉数字没那么难找吧还有,看到那个商店字库,感觉挺集中的,好像也没那么难找
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|虎纹猫家园

GMT+8, 2024-7-5 00:39 , Processed in 0.026799 second(s), 12 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表