Language…
17 users online:  AmperSam, asterkafton, Dangil, DanMario24YT,  Donut, dotCoockie, drkrdnk, EvilAdmiralKivi, Fiblizo, Guido_Keller, JezJitzu, LightAligns, mmmdoggy,  patcdr, Scags, timothy726, tOaO - Guests: 266 - Bots: 406
Users: 64,795 (2,377 active)
Latest user: mathew

Stripe image format - Tutorial.

ExGFX

I felt like making a thread covering stripe image format, since I believe none have been made before that explained it completely.

First of all:
This is a pretty useful Wiki page explaining it.
But just to try to make it simpler, I wanted to make a tutorial, so there. OK, let's get to the point.


1. What is stripe image format?

Basically all it is, are tiles on the screen loaded on layers 1-4, in SMW those very tiles are 8x8 and they can be loaded on layers 1-3.
Stripe image is found in other games too, such as Super Mario All-Stars. It's basically a tile-uploading technique, accessed with DMA.


2. Then, how does it work?

Stripe image tiles should be put in a table. One image made out of this format can consist out of many seperate 'lines', 'stripes'. Such a line basically consists out of two things:

1. A header
2. The tile data

The header is always 4 bytes, the tile data can vary.

Header.


Taken from the Wiki:

Quote
|Byte 1| |Byte 2| |Byte 3| |Byte 4|
EHHHYXyy yyyxxxxx DRllllll LLLLLLLL

E = End of data
HHH = Data destination (VRAM address, see below)
Xxxxxx = X coordinate
Yyyyyy = Y coordinate
D = Direction (0 = Horizontal, 1 = Vertical)
R = RLE (see below)
llllllLLLLLLLL = Length (amount of bytes to upload - 1)


As mentioned earlier, the header consists out of 4 bytes.

Byte 1:

E bit:
The 7th bit of this byte is the end of data bit. If this bit is set, the tile upload stops right there, so nothing after it will be uploaded anymore (in the stripe image routine, it returns when this bit is set).
Almost always, to specify that the end of data bit is set, value #$FF is used.
#$FF is an easy-to-recognize value and therefore, it's often used, even though any value between #$80 and #$FF should work.
So if you end a table, do not forget the #$FF behind it!

HHH bits:
This seems to determine on which layer the image is being uploaded.
If bit 5 is set and bits 4 and 6 are clear, tiles are uploaded on layer 1.
If bit bits 4 and 5 are set and bit 6 is clear, tiles are uploaded on layer 2.
If bits 4 and 6 are set and bit 5 is clear, tiles are uploaded on layer 3.

Y bit:
High bit of Y location of tiles being uploaded.
If it's set, the tile is loaded 256 pixels (1 screen) lower.

X bit:
High bit of X location of tiles being uploaded.
If it's set, the tile is loaded 256 pixels (1 screen) to the right.

yy:
Y location of tile, first two low bits.

Byte 2:

yyy:
Y location, other three low bits.

xxxxx:
X location, low bits. Mind that a line can contain 1F 8x8 tiles horizontally ; that's why there are 5 low bits.

Byte 3:
D:
Which direction the bytes should be uploaded to ; horizontal or vertical.
If this D bit is clear, tiles are uploaded like this:

Quote
[t][t]


If it's set:

Quote
[t]
[t]


Note: Horizontally, it goes to the right, and vertically, it sinks down. It cannot go left, or rise up.

Mind that the X and Y position system still works the same when you use vertical direction, however, the X bits will now become Y bits and the Y bits will become X bits.

R:
RLE bit. If it's set, this repeats the tiles an X number of times, determined by the Length bits. A line should look like this then:

Quote
db $58,$20,$40,$06 ; Header of line, three tiles are uploaded.
db $A0,$15 ; However, since RLE is set, you only need to specify one tile. This tile will be uploaded three times.


This is basically a byte-saver. You're never forced to use it, but it comes in handy sometimes.

llllll:
Length of data bits (these are often unused). See byte 4 for more detail.
They form the upper bits of the bits used in byte 4, so that one can upload data for 100 (hex) bytes or more, in one single line.

Byte 4:
LLLLLLLL:

Length of data bits. The whole fourth byte is made of these bits. So, how does this work?

Each 8x8 tile needs 2 bytes of data, as can be seen under 'Tile data'.
Byte 4 basically tells the game what size the tile data is, so the correct amount of tiles can be uploaded.
If this byte doesn't match with the size of the tile data, there's a 99% chance some tiles will either not be uploaded, or, worse and much more common, everything glitches up.

So how do you know what this byte should contain?
Well, perhaps there are tricks to come around this with a good assembler, but basically you have to count how many bytes the tiles you want to upload take up. Then you subtract that by one, because:

Fourth byte of the header = size of tile data in bytes, - 1.

The -1 does not apply for RLE.


A good code would be this:

Quote
db $58,$20,$00,$03
db $50,$14
db $50,$54


Bad code:

Quote
db $58,$20,$00,$01
db $50,$14
db $50,$54


Make sure they match!

Tile data:


This should be pretty simple. Each tile consists out of two bytes ; the tile-to-use byte, and the properties byte.

Byte 1:
Specifies which 8x8 graphic to use for the tile. Often it's easier to view if the GFX are uncompressed. When hacking SMW, AllGFX.bin is often a good choice for this. Note : Does not specify the graphics page, this is what the properties byte does.

Byte 2:
The properties byte. For any game this is in YXPCCCTT format.

NOTE: DO NOT CONFUSE WITH YXPPCCCT!! This is used for sprites, not layers.



Y:
Flipping the tile vertically wise.

X:
Flipping the tile horizontally wise.

P:
Priority bit. If set, it might go in front of more layers and/or sprites, than it did before. Layers only have one, as opposed to sprites.

CCC:
Palette to use.
For SMW, layers 1 and 2 can only uses palettes 0-7, 16 colours per palette.
Layer 3 is a story on its own: It also uses 8 palettes, but these have only 4 colours each. It shares palettes 0 and 1 with layers 1 and 2.
If CCC is:
Quote
000 = Palette 0
001 = Palette 1
010 = Palette 2
011 = Palette 3
100 = Palette 4
101 = Palette 5
110 = Palette 6
111 = Palette 7


The same goes for layer 3, but mind that you don't add 16 colours up per palette, but only 4. So palette 1 for layer 3 would be colours 4-7 from palette 0. Also note that for each palette, the first colour is always transparent. Yes, for layer 3 too.

TT:
Graphic page to use. Layers have 2 of these bits, as opposed to sprites, which only have 1, which means layers could use many GFX at the same time. However, SMW leaves bit 1 (the upper T bit) unused :[.
This could be called the high bit of the graphic-to-use-for-the-tile bits, located in the first byte of the tile data.



Okay, so we covered that up.

More examples
Here's a handful of example codes that are valid.

One simple line:
Quote
db $53,$A0,$00,$03 ; Layer 3, no RLE, 2 tiles.
db $A0,$04 ; Tile A0, palette 1 (layer 3, so colours 4-7, palette 0)
db $A0,$44 ; Tile A0, palette 1, X-flipped (layer 3, so colours 4-7, palette 0)

db $FF ; End table.


Two lines:
Quote
db $55,$83,$00,$07 ; Layer 3, no RLE, 4 tiles
db $B8,$18 ; Tile B8, palette 6
db $B9,$18 ; Tile B9, palette 6
db $B9,$58 ; Tile B9, palette 6, X flip
db $B8,$58 ; Tile B8, palette 6, X flip

db $55,$A3,$00,$07 ; Layer 3, no RLE, 4 tiles (one line lower than before)
db $B8,$98 ; Tile B8, palette 6, Y flip
db $B9,$98 ; Tile B9, palette 6, Y flip
db $B9,$D8 ; Tile B9, palette 6, X and Y flip
db $B8,$D8 ; Tile B8, palette 6, X and Y flip

db $FF


Using RLE:
Quote
db $58,$20,$40,$08 ; Layer 3, RLE, 4 tiles
db $30,$10 ; Tile 30, palette 4. Repeat 4 times because of RLE.

db $FF

--------> Don't follow "Find Roy's Dignity", my hack. Because it's pretty outdated. <--------
This is freaking amazing.

Couple questions though...


HHH: Just to clarify... Stripe Image Format can be set to draw to Layer 1, 2 OR 3, but the Layer 3 image data in the Rom is all set to draw just to Layer 3, right? Could you change that without problems, or does Layer 3 data ONLY get to draw on Layer 3?

CCC: Concerning palette data for Layer 3... You said it only uses 4 colors per "palette", and "Palette 1" would be colors 4 through 7 on Palette 0. Does this mean "Palette 2" is colors 8 through B on Palette 0, or does it loop to colors 0 through 3 on Palette 1 instead? (And so on and so forth.)

TT: You said the "upper T bit" in SMW is unused. Does this mean unused in the original SMW, or does this mean if we actually set it, nothing will happen? And if the former, can we assume this gives us 4 Graphics Pages total to load tiles from (00, 01, 10, 11)? I'm also curious as to which graphics pages these are (we all know Layer 3 uses a different set of graphics banks than the normal level), but I'm guessing we can find that out with a debugger and by looking at the VRam data, and the two pages after the first two *actually used* Layer 3 pages would be the ones used of the upper T bit was set for TT.

Bonus question: In IRC you mentioned something about re-pointing Layer 3 pointers in order to use custom Layer 3 images. Which Rom addresses would you be re-pointing, and how would you tell the game to use that for Layer 3 on some level? Or are those questions for another thread? 'Cause if these two are off-topic you can disregard them or whatever. :o

Anyway, this is awesome info, and I definitely understand them better now than I did in IRC. Thank you! :)
It's me!!

High on life is the best high.
Quote
HHH: Just to clarify... Stripe Image Format can be set to draw to Layer 1, 2 OR 3, but the Layer 3 image data in the Rom is all set to draw just to Layer 3, right? Could you change that without problems, or does Layer 3 data ONLY get to draw on Layer 3?


Well, I've tried drawing on layer 1 and I wouldn't really recommend using this method to load tiles on layer 1, since it has a Map16 table (with graphics), which handles the layer 1 graphic data all on its own.
This means that, whenever you leave the screen and come back, the image is gone. This is also made so that layer 1 doesn't repeat every 2 screens, unlike layers 2 and 3.

Drawing on layer 2 with this method would work just fine though, since it repeats every 2 screens. Note that this only works with the 'normal' layer 2 setting ; if the layer 2 setting is 'Level', it's the same story as with layer 1. It's not recommended then. But with normal layer 2 it works just fine.

Quote
CCC: Concerning palette data for Layer 3... You said it only uses 4 colors per "palette", and "Palette 1" would be colors 4 through 7 on Palette 0. Does this mean "Palette 2" is colors 8 through B on Palette 0, or does it loop to colors 0 through 3 on Palette 1 instead? (And so on and so forth.)


Palette 2 would be colours 8 through B, palette 3 colours C through F, etc.

Quote
TT: You said the "upper T bit" in SMW is unused. Does this mean unused in the original SMW, or does this mean if we actually set it, nothing will happen? And if the former, can we assume this gives us 4 Graphics Pages total to load tiles from (00, 01, 10, 11)? I'm also curious as to which graphics pages these are (we all know Layer 3 uses a different set of graphics banks than the normal level), but I'm guessing we can find that out with a debugger and by looking at the VRam data, and the two pages after the first two *actually used* Layer 3 pages would be the ones used of the upper T bit was set for TT.


Something will happen, but the tiles being loaded are basically rubbish. Yes, it would allow for 4 graphic files with FF tiles each.
I'm not sure why SMW doesn't use it, maybe there's a specific reason for it...
But I thought SMAS did use this although I need to double-check that.

Quote
Bonus question: In IRC you mentioned something about re-pointing Layer 3 pointers in order to use custom Layer 3 images. Which Rom addresses would you be re-pointing, and how would you tell the game to use that for Layer 3 on some level?


Er, yes, there's a pointer at SNES address $059000 and it ends at $059086. It's 24-bit.
It points to all the tileset specific layer 3 backgrounds in SMW, such as the cage, castle windows, water, etc.

The pointer's pattern is a bit like this:
Quote

org $059000 ; Start of pointer

dl $059549 ; Low and high tide tiles, tileset 0 (Normal 1)
dl $059549 ; Low tide tiles, tileset 0 (Normal 1)
dl $059087 ; Layer 3 cage, tileset 0 (Normal 1)
dl $059549 ; Low and high tide tiles, tileset 1 (Castle 1)
dl $059294 ; Layer 3 crusher, tileset 1 (Castle 1)
dl $059AE0 ; Layer 3 windows, tileset 1 (Castle 1)
dl $059549 ; Low and high tide tiles, tileset 2 (Rope 1)
dl $059549 ; Low tide tiles, tileset 2 (Rope 1)


Etc.
Just modify one 24-bit address of that pointer and repoint it to a custom table, to upload tiles on layer 3 from there.
The scrolling etc. might need some extra work, that's done by hijacking another routine.

Important edit:
It seems when RLE is set, the length byte is not 'amount of bytes to upload - 1' but rather just 'amount of bytes to upload'.
Sorry for this error. O_o. I fixed it in the first post now.
--------> Don't follow "Find Roy's Dignity", my hack. Because it's pretty outdated. <--------
That tutorial is kickass, I attempted that thing myself and got a surprising result:


Thanks to Fuzzyfreak for setting up the base patch for me. ;D
Who someone annihilate Buu-Huu
>_>
<_<

Nonetheless, this tutorial looks quite awesome. Perhaps I'll look into it one day and mess around with it.
Just to clarify a few things:

The system can be used to upload to any layer, even multiple layers at once. An example of the latter thing is the ending; the enemy names on Layer 3 and the blackness on Layer 1 are stored in the same Stripe Image. Feel free to upload to Layer 1 as well, just as long as you know what you're doing.

Technically, the system could be used to upload data to other parts of VRAM too. The two first bytes (with the exception of the E bit) are actually the VRAM address to upload to.

As for the upper T bit: Layers 1 and 2 can actually use up to 1024 tiles, twice as many as used in SMW. (The same goes for layers 3 and 4.) The problem is the limited VRAM space.

Another thing of interest is RAM address $7E/0012 and Dynamic VRAM Upload.

When you write to $12, it will load an image from a set of Stripe Images. You could repoint ones you're not using in your hack to suit your own purposes.

Dynamic VRAM Upload allows uploading non-hardcoded Sprite Images. The game uses this for, among other things, replacing the graphics when a block is changed (such as when a ? block becomes a brown block). It could be used for a lot of fun.

Details about Dynamic VRAM Upload, the list of $12 images and the $12 pointer table address can be found here.
Ok, this is a bit confuzing for me.
I would like to make a layer 3image, but i would have no idea where to start. Could someone dumb it up for me as much as possible, listing exactly what i would have to do, what tools, limitations, etc. It would help me out alot. Thanks. :)
<TLMB> I use YY-CHR to edit DNA
-Question answered by smalls on IRC-

Question was about the "l" bits, and:

"l" bits are the upper bits of the length
Originally posted by spigmike
-Question answered by smalls on IRC-

Question was about the "l" bits, and:

"l" bits are the upper bits of the length


I know, they're often not used though, since they're used for tiles being uploaded which should contain over 100 bytes.
If you want though, I'll add it in the first post.
--------> Don't follow "Find Roy's Dignity", my hack. Because it's pretty outdated. <--------
I would, just because I encountered it when working with the Overworld border, so I'm sure several SMW stripe images may have them.

This kind of information is extremely useful, and holds a lot of great potential. Especially for people who want custom Layer 3 in there hacks. I wish someone would make a very detailed tutorial explaining how to do this one step at a time using examples and pictures describing the procedures of how to use this stripe image format....


(sigh)But I guess that's never gonna happen anytime soon:(

UPDATE!!!
WARNING! BELOW CODE IS NOT COMPATIBLE WITH EMULATORS BESIDES ZSNES WHEN PLACED OUTSIDE OF NMI!


I had not yet explained how to actually load tiles on screen - I only explained the term stripe image format, and how to edit tables, which were written in this format.

To load the tiles on screen, you need the following things:

- A DMA routine
- A stripe image table
- A pointer (can eventually point to multiple different images)
- Eventually getting the required set-up for the DMA routine.

Now, I'm going to base this routine off something SMW uses.
You might find a better way to upload tiles on screen, but this post will basically explain it with a method similar to how SMW did it.

This post will explain how to do this, through levelASM. We're going to use Ersanio's levelASM for this.

Okay - so before I'm going to explain the DMA routine, we need to set a few things up a bit.

First, we will create the stripe image table.

Quote

LayerImg:
db $53,$E0,$40,$08 ; Layer 3. RLE, 4 tiles.
db $00,$38 ; Tile 00, palette 6. Priority = 1.

db $FF ; End.


You can, of course, edit this table to your wishes - we will use this one as example now.

Now, for the DMA routine, we will want to have a pointer that points to the table.
Simply:

Quote

LayerPtr:
dw LayerImg


(We don't need a 24-bit pointer, a 16-bit one will be sufficient for levelASM)

Put the pointer and the stripe image table at the end of the code - make sure you can never reach it normally (because this will likely cause complications).

Before starting the DMA routine, we still have to set a few things up.
We're using the cell for level 105.
The code for preparing for the DMA routine, should look like this:

Quote

level105:
PHB ; Preserve data bank.
PHK ; \ Transfer program bank (the bank in which the code is currently running)...
PLB ; / ... over to data bank (used for bank byte for 16-bit address operations such as LDA $89AB,x)
LDA LayerPtr+1 ; \ Load high byte of 16-bit address of stripe image table
PHA ; / Push this byte.
LDA LayerPtr ; \ Load low byte of 16-bit address of stripe image table
PHA ; / Push this byte
PHK ; \ Get program bank...
PLA ; / ... into A.


Now, for the DMA routine. You don't need to understand it completely, just put it in your code.

Quote

REP #$10 ; Index = 16-bit.
STA $4314 ; Program bank = Bank byte of DMA transfer source.
LDY.w #$0000

LoopAndReturn:
LDA ($01,s),y ; \ Get first byte of stripe image line (byte 1 of header).
BPL DMADMA ; / If bit 7 = clear, branch.
SEP #$30 ; Index & Accumulator = 8 bit.
PLB ; \ Pull useless bytes from LayerPtr...
PLB ; / .. and LayerPtr+1 into the data bank.
PLB ; Pull old data bank back.
RTS ; Terminate.

DMADMA:
STA $04 ; Store byte 1 of header into $04.
INY ; Increase Y index.
LDA ($01,s),y ; \ Get byte 2 of header.
STA $03 ; / Store into $03.
INY ; Increase Y index.
LDA ($01,s),y ; Get byte 3 of header...
STZ $07 ; $07 = #$00.
ASL ; ASL (direction bit will eventually go into carry)
ROL $07 ; ROL into $07. (Result = #$01 if direction bit was set)
LDA #$18 ; \ Address to write to...
STA $4311 ; / = $2118.
LDA ($01,s),y ; \ Get byte 3 of header.
AND #$40 ; | Keep RLE bit intact, clear all others.
LSR ; |
LSR ; | Divide by 8.
LSR ; |
STA $05 ; / Store into $05.
STZ $06 ; $06 = #$00.
ORA #$01 ; \ Set bit 0.
STA $4310 ; / Store into $4310. (2 registers write once. If RLE set, do not move address to load data from until DMA transfer size becomes 0)
REP #$20 ; Accumulator = 16-bit.
LDA $03 ; \ Load $03 and $04.
STA $2116 ; / Store into $2116 (VRAM Address).
LDA ($01,s),y ; \ Get bytes 3 and 4 of header.
XBA ; | Flip Accumulators A and B.
AND.w #$3FFF ; | Keep 'length' bits intact, clear RLE and direction bit.
TAX ; / Length -> X index.
INX ; Increase X by 1.
INY ; \
INY ; / Increase Y by two.
TYA ; Y -> A.
CLC ; \ ... add with...
ADC $01,s ; | ... start of stripe image table...
STA $4312 ; | Store into DMA source address.
STX $4315 ; / Store length into DMA size address.
LDA $05 ; \ If $05 = #$00...
BEQ NoRLE ; / ... no RLE, branch.
SEP #$20 ; Accumulator = 8-bit.
LDA $07 ; \ $07 =
STA $2115 ; / $2115.
LDA #$02 ; \ DMA Channel to enable...
STA $420B ; / ... is channel 1.
LDA #$19 ; \ Address to write to...
STA $4311 ; / = $2119.
REP #$21 ; Accumulator = 16-bit. Carry = set.
LDA $03 ; \ Load $03 and $04.
STA $2116 ; / Store into $2116 (VRAM Address)
TYA ; \ Y -> A.
ADC $01,s ; | ... add with start of stripe image table...
INC ; | Increment by 1.
STA $4312 ; | Store into DMA source address.
STX $4315 ; / Store length into DMA size address.
LDX.w #$0002 ; X = 0002.

NoRLE:
STX $03 ; X into $03 - $04.
TYA ; Y -> A.
CLC ; \ Add with...
ADC $03 ; | $03 - $04...
TAY ; / Back into Y again. New line.
SEP #$20 ; Accumulator = 16-bit.
LDA $07 ; \ Load $07...
ORA #$80 ; | ... set bit 7, regardless of whether it already was or not.
STA $2115 ; / Store into $2115.
LDA #$02 ; \ DMA Channel to enable...
STA $420B ; / ... is channel 1.
JMP LoopAndReturn ; Loop, eventually terminate.


Yes, it is a ridiculously long code, but it works. If you find a better method, don't hesitate to use your own.

==============================================

By now, the whole code, which should work for level 105 only, should look like this:

Quote


level105:
PHB ; Preserve data bank.
PHK ; \ Transfer program bank (the bank in which the code is currently running)...
PLB ; / ... over to data bank (used for bank byte for 16-bit address operations such as LDA $89AB,x)
LDA LayerPtr+1 ; \ Load high byte of 16-bit address of stripe image table
PHA ; / Push this byte.
LDA LayerPtr ; \ Load low byte of 16-bit address of stripe image table
PHA ; / Push this byte
PHK ; \ Get program bank...
PLA ; / ... into A.

REP #$10 ; Index = 16-bit.
STA $4314 ; Program bank = Bank byte of DMA transfer source.
LDY.w #$0000

LoopAndReturn:
LDA ($01,s),y ; \ Get first byte of stripe image line (byte 1 of header).
BPL DMADMA ; / If bit 7 = clear, branch.
SEP #$30 ; Index & Accumulator = 8 bit.
PLB ; \ Pull useless bytes from LayerPtr...
PLB ; / .. and LayerPtr+1 into the data bank.
PLB ; Pull old data bank back.
RTS ; Terminate.

DMADMA:
STA $04 ; Store byte 1 of header into $04.
INY ; Increase Y index.
LDA ($01,s),y ; \ Get byte 2 of header.
STA $03 ; / Store into $03.
INY ; Increase Y index.
LDA ($01,s),y ; Get byte 3 of header...
STZ $07 ; $07 = #$00.
ASL ; ASL (direction bit will eventually go into carry)
ROL $07 ; ROL into $07. (Result = #$01 if direction bit was set)
LDA #$18 ; \ Address to write to...
STA $4311 ; / = $2118.
LDA ($01,s),y ; \ Get byte 3 of header.
AND #$40 ; | Keep RLE bit intact, clear all others.
LSR ; |
LSR ; | Divide by 8.
LSR ; |
STA $05 ; / Store into $05.
STZ $06 ; $06 = #$00.
ORA #$01 ; \ Set bit 0.
STA $4310 ; / Store into $4310. (2 registers write once. If RLE set, do not move address to load data from until DMA transfer size becomes 0)
REP #$20 ; Accumulator = 16-bit.
LDA $03 ; \ Load $03 and $04.
STA $2116 ; / Store into $2116 (VRAM Address).
LDA ($01,s),y ; \ Get bytes 3 and 4 of header.
XBA ; | Flip Accumulators A and B.
AND.w #$3FFF ; | Keep 'length' bits intact, clear RLE and direction bit.
TAX ; / Length -> X index.
INX ; Increase X by 1.
INY ; \
INY ; / Increase Y by two.
TYA ; Y -> A.
CLC ; \ ... add with...
ADC $01,s ; | ... start of stripe image table...
STA $4312 ; | Store into DMA source address.
STX $4315 ; / Store length into DMA size address.
LDA $05 ; \ If $05 = #$00...
BEQ NoRLE ; / ... no RLE, branch.
SEP #$20 ; Accumulator = 8-bit.
LDA $07 ; \ $07 =
STA $2115 ; / $2115.
LDA #$02 ; \ DMA Channel to enable...
STA $420B ; / ... is channel 1.
LDA #$19 ; \ Address to write to...
STA $4311 ; / = $2119.
REP #$21 ; Accumulator = 16-bit. Carry = set.
LDA $03 ; \ Load $03 and $04.
STA $2116 ; / Store into $2116 (VRAM Address)
TYA ; \ Y -> A.
ADC $01,s ; | ... add with start of stripe image table...
INC ; | Increment by 1.
STA $4312 ; | Store into DMA source address.
STX $4315 ; / Store length into DMA size address.
LDX.w #$0002 ; X = 0002.

NoRLE:
STX $03 ; X into $03 - $04.
TYA ; Y -> A.
CLC ; \ Add with...
ADC $03 ; | $03 - $04...
TAY ; / Back into Y again. New line.
SEP #$20 ; Accumulator = 16-bit.
LDA $07 ; \ Load $07...
ORA #$80 ; | ... set bit 7, regardless of whether it already was or not.
STA $2115 ; / Store into $2115.
LDA #$02 ; \ DMA Channel to enable...
STA $420B ; / ... is channel 1.
JMP LoopAndReturn ; Loop, eventually terminate.

LayerPtr:
dw LayerImg

LayerImg:
db $53,$E0,$40,$08
db $00,$38
db $FF


The result should be something along the lines of this:

Notice the zeroes.

Eventually, you might prefer that it will only run once. Too much DMA could cause a lot of lag! This will be the case if your stripe image table is too large.
You could unused RAM addresses for this, or you could also use a generator / sprite init routine.

This code should also be compatible with sprites. This means that you can let a sprite create background images with this routine, if you ever wanted that.
You might want to use $7F837D for that, however... but I'm not sure if it can also fulfill large background requests.

That was my update... I hope this can be useful to people.
--------> Don't follow "Find Roy's Dignity", my hack. Because it's pretty outdated. <--------
I can't believe you put an use to LDA ($xx,s),y. x_x
Originally posted by Roy

level105:
PHB ; Preserve data bank.
PHK ; \ Transfer program bank (the bank in which the code is currently running)...
PLB ; / ... over to data bank (used for bank byte for 16-bit address operations such as LDA $89AB,x)
LDA LayerPtr+1 ; \ Load high byte of 16-bit address of stripe image table
PHA ; / Push this byte.
LDA LayerPtr ; \ Load low byte of 16-bit address of stripe image table
PHA ; / Push this byte
PHK ; \ Get program bank...
PLA ; / ... into A.

REP #$10 ; Index = 16-bit.
STA $4314 ; Program bank = Bank byte of DMA transfer source.
LDY.w #$0000

LoopAndReturn:
LDA ($01,s),y ; \ Get first byte of stripe image line (byte 1 of header).
BPL DMADMA ; / If bit 7 = clear, branch.
SEP #$30 ; Index & Accumulator = 8 bit.
PLB ; \ Pull useless bytes from LayerPtr...
PLB ; / .. and LayerPtr+1 into the data bank.
PLB ; Pull old data bank back.
RTS ; Terminate.

DMADMA:
STA $04 ; Store byte 1 of header into $04.
INY ; Increase Y index.
LDA ($01,s),y ; \ Get byte 2 of header.
STA $03 ; / Store into $03.
INY ; Increase Y index.
LDA ($01,s),y ; Get byte 3 of header...
STZ $07 ; $07 = #$00.
ASL ; ASL (direction bit will eventually go into carry)
ROL $07 ; ROL into $07. (Result = #$01 if direction bit was set)
LDA #$18 ; \ Address to write to...
STA $4311 ; / = $2118.
LDA ($01,s),y ; \ Get byte 3 of header.
AND #$40 ; | Keep RLE bit intact, clear all others.
LSR ; |
LSR ; | Divide by 8.
LSR ; |
STA $05 ; / Store into $05.
STZ $06 ; $06 = #$00.
ORA #$01 ; \ Set bit 0.
STA $4310 ; / Store into $4310. (2 registers write once. If RLE set, do not move address to load data from until DMA transfer size becomes 0)
REP #$20 ; Accumulator = 16-bit.
LDA $03 ; \ Load $03 and $04.
STA $2116 ; / Store into $2116 (VRAM Address).
LDA ($01,s),y ; \ Get bytes 3 and 4 of header.
XBA ; | Flip Accumulators A and B.
AND.w #$3FFF ; | Keep 'length' bits intact, clear RLE and direction bit.
TAX ; / Length -> X index.
INX ; Increase X by 1.
INY ; \
INY ; / Increase Y by two.
TYA ; Y -> A.
CLC ; \ ... add with...
ADC $01,s ; | ... start of stripe image table...
STA $4312 ; | Store into DMA source address.
STX $4315 ; / Store length into DMA size address.
LDA $05 ; \ If $05 = #$00...
BEQ NoRLE ; / ... no RLE, branch.
SEP #$20 ; Accumulator = 8-bit.
LDA $07 ; \ $07 =
STA $2115 ; / $2115.
LDA #$02 ; \ DMA Channel to enable...
STA $420B ; / ... is channel 1.
LDA #$19 ; \ Address to write to...
STA $4311 ; / = $2119.
REP #$21 ; Accumulator = 16-bit. Carry = set.
LDA $03 ; \ Load $03 and $04.
STA $2116 ; / Store into $2116 (VRAM Address)
TYA ; \ Y -> A.
ADC $01,s ; | ... add with start of stripe image table...
INC ; | Increment by 1.
STA $4312 ; | Store into DMA source address.
STX $4315 ; / Store length into DMA size address.
LDX.w #$0002 ; X = 0002.

NoRLE:
STX $03 ; X into $03 - $04.
TYA ; Y -> A.
CLC ; \ Add with...
ADC $03 ; | $03 - $04...
TAY ; / Back into Y again. New line.
SEP #$20 ; Accumulator = 16-bit.
LDA $07 ; \ Load $07...
ORA #$80 ; | ... set bit 7, regardless of whether it already was or not.
STA $2115 ; / Store into $2115.
LDA #$02 ; \ DMA Channel to enable...
STA $420B ; / ... is channel 1.
JMP LoopAndReturn ; Loop, eventually terminate.

LayerPtr:
dw LayerImg

LayerImg:
db $53,$E0,$40,$08
db $00,$38
db $FF


How do I go about inserting that code into my SMW?
You use levelASM.
Like Ersanio's levelASM.
--------> Don't follow "Find Roy's Dignity", my hack. Because it's pretty outdated. <--------
This is great, I've coded a custom Layer 3! But how do I change the scroll rate so it doesn't follow Mario all the time?

Edit:If that's too hard, then how do I put it behind all other layers?
For your first question: Clicky.

For your second question, that's relatively simple. Move all layers to the mainscreen and layer 3 to the subscreen, or so, like this:

Quote
REP #$20
LDA #$041B
STA $212C
SEP #$20


Or so.
--------> Don't follow "Find Roy's Dignity", my hack. Because it's pretty outdated. <--------
Here's a patch I made that makes the routine I posted earlier this thread completely redundant.
Using levelASM for stripe image is not necessary anymore - use this patch instead!
The only thing you still need to have knowledge of is how stripe image tables work exactly, and of course, you need to be able to handle this patch.
It uploads the image once every 8 frames, so it shouldn't cause a lot of slowdown either.

Plannings for v1.1:
- Init XY and scrolling settings for Layer 3. (So levelASM will become completely redundant.)
- Perhaps only upload once (level init) since that's always the best way to do it anyway.
--------> Don't follow "Find Roy's Dignity", my hack. Because it's pretty outdated. <--------
This is really nice, Roy! However, for some reason applying your patch to my ROM makes my game freeze whenever I die (also no music plays). I guess it is because of the Custom Death Routine I made for my game. At first I thought it was a free space conflict, but changing the Free Space Adress didn't do anything.

Also when doing verion 1.1 I recommend you to somehow implement a method that makes the stripe image reappear after you hit a message box (currently it just disappears as it did with level.asm).
Feel free to visit my website/blog - it's updated rarely, but it looks pretty cool!

ExGFX