📄 qsnesdoc.html
字号:
<P><TT>Read # Button Read # Button</TT>
<BR><TT> 1 B
9 A</TT>
<BR><TT> 2 Y
10 X</TT>
<BR><TT> 3 Select 11
L (Top-left)</TT>
<BR><TT> 4 Start 12
R (Top-right)</TT>
<BR><TT> 5 Up
13 -</TT>
<BR><TT> 6 Down 14
-</TT>
<BR><TT> 7 Left 15
-</TT>
<BR><TT> 8 Right 16
-</TT>
<P>1 is returned if the button is currently pressed.
<P>If the pattern was similar to that of the NES, reads 17 to 32 would
return the state of joypads 3 and 4. However, zsKnight says that
read #17 returns whether or not the joypad is connected (0 if not connected.)
<BR>
<CENTER><A NAME="RegDMA"></A><FONT SIZE=+2>DMA Registers</FONT></CENTER>
<CENTER>See also <A HREF="#DMATransfers">DMA Transfer Information</A>.</CENTER>
<BR><A NAME="Reg420B"></A><B><FONT FACE="Lucida Console">Register $420B:
Start DMA transfer (1b/W)</FONT></B>
<PRE>76543210 Bits indicate which DMA transfer(s) to initiate
(1=Transfer, 0=Do not transfer)</PRE>
After setting up a DMA transfer, a 1 should be written to the bit corresponding
to the channel you want to use for the transfer. After the 1 is written,
the CPU is paused while the DMA transfer takes place; each byte transferred
takes one clock cycle. Note that a DMA and HDMA transfer cannot be
done on the same channel: if a DMA transfer is initiated while a HDMA channel
is enabled, nothing will happen.
<P><A NAME="Reg420C"></A><B><FONT FACE="Lucida Console">Register $420C:
Enable H-DMA transfer (1b/W)</FONT></B>
<PRE>76543210 Bits indicate which HDMA channel(s) to enable
(1=Enable transfers, 0=Disable transfers)</PRE>
After setting up the HDMA transfer(s), a 1 should be written to all the
bits corresponding to the channels you want enabled. Every scanline,
HDMA transfers will occur automatically.
<P><A NAME="Reg43x0"></A><B><FONT FACE="Lucida Console">Register $43?0:
DMA control register (1b/W)</FONT></B>
<PRE>da-fittt d:(DMA only) 0=read from CPU memory, write to $21?? registers
1=read from $21?? registers, write to CPU memory
a:(HDMA only) 0=Absolute addressing
1=Indirect addressing
i:fixed CPU memory address (1=fixed; 0=inc/dec depending on bit i)
f:0=increment CPU memory pointer by 1 after every read/write 1=decrement
t:DMA/HDMA transfer type</PRE>
The 'i' and 'f' bits I had mixed up for quite some time... actually they
were backwards in another doc, and so my emu's graphics were screwed for
a long time... Bit 3 means a fixed address, so that the same byte
is transferred every time.
<BR>This register controls the way DMA transfers take place. The
t bits decide the 'mode' of the transfer. To describe how this works,
suppose the bytes of data to be transferred are $01 $23 $45 $67 $89, and
the register to write to is $2118. Assuming f=0 and i=0:
<PRE>Transfer Type Order of transfer
000: 1 reg $01->$2118 $23->$2118 $45->$2118 $67->$2118 $89->$2118
001: 2 regs $01->$2118 $23->$211<B>9</B> $45->$2118 $67->$211<B>9</B> $89->$2118
010: 1 reg write twice $01->$2118 $23->$2118 $45->$2118 $67->$2118 $89->$2118
011: 2 regs write twice $01->$2118 $23->$2118 $45->$211<B>9</B> $67->$211<B>9</B> $89->$2118
100: 4 regs $01->$2118 $23->$211<B>9</B> $45->$211<B>A</B> $67->$211<B>B</B> $89->$2118
101, 110, 111 unknown behavior</PRE>
Transfer modes 0 and 2 appear to be the same, but are different in HDMA
mode. I may have modes 0 and 2 backwards (i.e. 0=1 reg write twice.).
<P><A NAME="Reg43x1"></A><B><FONT FACE="Lucida Console">Register $43?1:
DMA Destination Address (1b/W)</FONT></B>
<PRE>bbbbbbbb Add this value to $2100 to get the destination address
(Notice that this restricts what registers you can send to)</PRE>
This is where data will be sent to during a DMA transfer, unless register
$43?0 bit 7 is 1, in which case this will be the source register.
In an HDMA transfer, this is <I>always</I> the source address.
<P><A NAME="Reg43x2"></A><B><FONT FACE="Lucida Console">Register $43?2/$43?3/$43?4:
DMA Source Address (3b/W)</FONT></B>
<PRE>aaaaaaaa aaaaaaaa aaaaaaaa Full 24-bit address of DMA transfer source address</PRE>
This is where data will be read from during a DMA transfer, unless register
$43?0 bit 7 is 1, in which case this will be the destination address.
In an HDMA transfer, this is <I>always</I> the source address, since HDMA
is unidirectional.
<P>Qwertie's horror story: I fixed tons of graphics glitches when I figured
this out: you must increment the value in this register after a DMA
transfer. That is, transferring $10 bytes forward starting at $00E000
would cause this register to be $00E010 afterward. Also, zsKnight
has implied that DMA transfers wrap on a bank boundary; i.e. $43?4 is not
changed during a transfer.
<P><A NAME="Reg43x5"></A><B><FONT FACE="Lucida Console">Register $43?5/$43?6/$43?7:
Bytes to Transfer (2b or 3b/W)</FONT></B>
<PRE>???????? aaaaaaaa aaaaaaaa a: amount of bytes to transfer (DMA only)
In HDMA, purpose is unknown.</PRE>
When using DMA, set this to the number of bytes you want transferred.
Note: if set to 0, 65536 bytes of data will be transferred. I am
not sure whether this register should be set to 0 after the transfer.
<P>In HDMA, it is not neccessary for a SNES programmer to write to this
register. This register probably contains a pointer the next or previous
value written. Based on circumstantial evidence from one game, this
register may contain the value pointer minus one in regular HDMA (e.g.
if this register contained $123456, it would mean that the next value to
be written was contained in $123457.) In indirect HDMA, this register
probably contains the pointer to the value after indirection.
<P>When using regular HDMA, either the bank is copied to this register
or the bank stored here is ignored. In Indirect HDMA, however, the
bank address has to be set manually, and the SNES will automatically determine
the offset.
<P><A NAME="Reg43x8"></A><B><FONT FACE="Lucida Console">Register $43?8/$43?9:
HDMA count pointer (2b/RW?)</FONT></B>
<P>This register does not have to be directly written by the programmer;
the SNES updates it automatically.
<P>I'm calling this register a count pointer because it probably points
to the byte in the HDMA table that contains the count value until the next
segment of the HDMA table is executed. This applies to both HDMA
and Indirect HDMA. (In Indirect HDMA, this register could also be
called the "pointer before indirection".)
<P>Since this is a two and not three-byte register, the bank value should
be taken from <A HREF="#Reg43x2">$43?4</A>.
<P><A NAME="Reg43xA"></A><B><FONT FACE="Lucida Console">Register $43?A:
Scanlines left (1b/RW?)</FONT></B>
<P>This register does not have to be directly written by the programmer.
<P>This register is a countdown that contains the number of scanlines remaining
until the next segment of a HDMA table is executed. When this counter
reaches 0, the pointer contained in <A HREF="#Reg43x8">register $43?8</A>
is advanced to the next count value in the HDMA table, and this register
is loaded with the count value for the next segment of the table.
<P><A NAME="Reg43xA"></A><B><FONT FACE="Lucida Console">Register $2180/$2181/$2182/$2183:
WRAM access (4b/RW)</FONT></B>
<P><TT>$2180 dddddddd d: Data byte</TT>
<BR><TT>$2181 ???????x xxxxxxxx xxxxxxxx x: address</TT>
<P>This isn't a DMA register at all, but is often used in conjunction with
DMA to do memory-to-memory or memory-fill operations. This is a fairly
simple register, but I'm documenting it here because it's not fully covered
anywhere else. A write or a read to this register causes a read/write
to the specified address, and every time a read/write occurs, the address
part of the register is incremented by one, including the MSB.
<P>Important: The x bits (address) seem to actually form a RAM address,
NOT a CPU address. Therefore, the highest 7 bits of the register
are ignored, since the SNES has only 128 KB of RAM.
<P><A NAME="DMATransfers"></A><FONT SIZE=+3>DMA Transfer Basics</FONT>
<BR>
<BR>First of all, here's a list of DMA (Direct Memory Access) registers:
<P>$43?0: DMA control register
<BR>$43?1: DMA destination register ($21xx)
<BR>$43?2/$43?3/$43?4: DMA source address
<BR>$43?5/$43?6: Number of bytes to transfer
<BR>$420B: Start DMA transfer
<BR>$420C: Enable HDMA transfers
<P>There are 8 DMA channels, numbered 0 to 7. Simply replace the
question mark with the number of the channel you want to use. Each
channel can be used independently of the others.
<P>There are two basic types of DMA: DMA and HDMA. Normal DMA is
quite straightforward. The SNES program simply puts a source address into
$43?2, a destination address into $43?1 and the number of bytes to copy
into $43?5. The reason $43?1 is only a one-byte register is because
the high byte is always $21; e.g. if you set it to $18, the transfer destination
will be $2118. The <A HREF="#Reg43x0">DMA control register</A> controls
how the bytes will be transferred. Using the previous example, setting
it to one(1) will cause bytes written to alternate between $2118 and $2119.
Use $43?0 to start a transfer. For instance, if you were using DMA
channel 1 ($4310, etc.), you would set bit one in that register. (e.g.
LDA #$2 \ STA $420B.)
<P>HDMA is a powerful, and largely undocumented, tool for generating special
effects. It is used often by commercial games and demos alike. Its
most common use is to change one or more of the scroll registers mid-frame;
some games also change the video mode or palette mid-frame with it.
It is set up similar to DMA, except that $43?5 is ignored and probably,
bits 3, 4, 5 and 7 of $43?0 are ignored.
<P><B>Normal HDMA</B>
<P>There are two types of HDMA, controlled by bit 6 of register $43?0.
"Normal" HDMA is the simplest.
<P>The source address register points to an "HDMA table". Such a
table consists of a list of "delay counts" and "write values". For
example, consider the following HDMA table, which is an abridgement of
what I use in my demo QwertEmu.smc. It the destination is the BG1
scroll register.
<P><TT>hdma_table:</TT>
<BR><TT> .dcb 3 : .dcw 1
; This makes the cool wavey BG effect.</TT>
<BR><TT> .dcb 4 : .dcw 2</TT>
<BR><TT> .dcb 6 : .dcw 3</TT>
<BR><TT> .dcb 7 : .dcw 4</TT>
<BR><TT> .dcb 6 : .dcw 3</TT>
<BR><TT> .dcb 4 : .dcw 2</TT>
<BR><TT> .dcb 3 : .dcw 1</TT>
<BR><TT> .dcb 2
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -