Slide 1

Slide 1 text

MAKING NES GAMES Kevin Zurawel (@kzurawel) or: Assembly for People Who Think Assembly
 Is Weird and Hard

Slide 2

Slide 2 text

WHAT IS “NES?”

Slide 3

Slide 3 text

NINTENDO ENTERTAINMENT SYSTEM (1985)

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Photo by Eckhard Pecher, CC-BY 2.5

Slide 8

Slide 8 text

DONKEY KONG (1981)

Slide 9

Slide 9 text

RADAR SCOPE (1980)

Slide 10

Slide 10 text

DONKEY KONG (1981)

Slide 11

Slide 11 text

“The product had to be priced so that parents would buy it. No matter how great the games, if mothers thought they were too expensive the machine would never take off. -Osamu Katayama, “Japanese Business into the 21st Century”

Slide 12

Slide 12 text

¥9800 (~$40)

Slide 13

Slide 13 text

arcadeblogger.com/2016/05/13/arcade-factory/

Slide 14

Slide 14 text

MISSILE COMMAND (ATARI, 1980) Total materials cost: $871.00 + assembly

Slide 15

Slide 15 text

1975

Slide 16

Slide 16 text

WHAT IS “ASSEMBLY?”

Slide 17

Slide 17 text

FIRST, A LITTLE BIT OF MATH (just a little!)

Slide 18

Slide 18 text

BITS & BYTES BIT 0 1 Two possible values

Slide 19

Slide 19 text

BITS & BYTES TWO BITS 00 01 10 11 Four possible values

Slide 20

Slide 20 text

BITS & BYTES BYTE (EIGHT BITS) 00000000 00000001 00000010 00000011 00000100 00000101 00000110 … 256 possible values

Slide 21

Slide 21 text

BITS & BYTES BYTE (EIGHT BITS) 11001011 256 possible values Bit 7 Bit 0

Slide 22

Slide 22 text

HEX HEXADECIMAL (HEX) DIGIT 0 1 2 3 4 5 6 7 8 9 A B C D E F 16 possible values

Slide 23

Slide 23 text

HEX TWO HEX DIGITS = ONE BYTE 00 01 02 03 04 05 06 07 08 09 0A 0B 0C … 256 possible values

Slide 24

Slide 24 text

16-BIT VALUES 16 BITS = 2 BYTES = 4 HEX DIGITS 00110001 11010110 “High” byte “Low” byte $31 D6 65,536 possible values

Slide 25

Slide 25 text

REPRESENTING NUMBERS IN ASSEMBLY %01101010 ; BINARY $6A ; HEX 106 ; DECIMAL

Slide 26

Slide 26 text

WHAT IS “ASSEMBLY?” (for real this time)

Slide 27

Slide 27 text

MACHINE CODE E1 80 20 76 F9 C8 A9 7F 8D 00 02 20 80 F9 E1 80 20 84 F9 60 A9 55 85 78 A9 FF 85 01 24 01 A0 11 A2 23 A9 00 A5 78 F0 10 30 0E C9 55 D0 0A C0 11 D0 06 E0 23 50 02 F0 04 A9 76 85 00 A9 46 24 01 85 78 F0 0A 10 08 50 06 A5 78 C9 46 F0 04 A9 77 85 00 A9 55 85 78 24 01 A9 11 A2 23 A0 00 A4 78 F0 10 30 0E C0 55 D0 0A C9 11 D0 06 E0 23 50 02 F0 04 A9 78 85 00 A0 46 24 01 84 78 F0 0A 10 08 50 06 A4 78 C0 46 F0 04 A9 79 85 00 24 01 A9

Slide 28

Slide 28 text

ASSEMBLY LDX #$00 STX PPUCTRL STX PPUMASK STX $4010 DEX TXS BIT PPUSTATUS BIT SNDCHN LDA #$40

Slide 29

Slide 29 text

6502 INSTRUCTION SET ➤ ADC Add with Carry ➤ AND And with accumulator ➤ ASL Arithmetic Shift Left ➤ BCC Branch on Carry Clear ➤ BCS Branch on Carry Set ➤ BEQ Branch on result Equals zero ➤ BIT Bit Test ➤ BMI Branch on result Minus ➤ BNE Branch on result Not Equal zero ➤ BPL Branch on result Plus
 ➤ BRK Break ➤ BVC Branch on Overflow Clear ➤ BVS Branch on Overflow Set ➤ CLC Clear Carry flag ➤ CLD Clear Decimal flag ➤ CLI Clear Interrupt disable flag ➤ CLV Clear Overflow flag ➤ CMP Compare with accumulator ➤ CPX Compare with X register ➤ CPY Compare with Y register ➤ DEC Decrement memory ➤ DEX Decrement X register ➤ DEY Decrement Y register ➤ EOR Exclusive Or with accumulator ➤ INC Increment memory ➤ INX Increment X register ➤ INY Increment Y register ➤ JMP Jump to new location ➤ JSR Jump and Save Return address ➤ LDA Load Accumulator with value ➤ LDX Load X register with value ➤ LDY Load Y register with value ➤ LSR Logical Shift Right ➤ NOP No Operation ➤ ORA "OR" with Accumulator ➤ PHA Push Accumulator to stack ➤ PHP Push Processor status to stack ➤ PLA Pull Accumulator from stack ➤ PLP Pull Processor status from stack ➤ ROL Rotate bits Left ➤ RTI Return from Interrupt ➤ RTS Return from Subroutine ➤ SBC Subtract with Carry borrowing ➤ SEC Set Carry flag ➤ SED Set Decimal mode ➤ SEI Set Interrupt disable flag ➤ STA Store Accumulator in memory ➤ STX Store X register in memory ➤ STY Store Y register in memory ➤ TAX Transfer Accumulator to X register ➤ TAY Transfer Accumulator to Y register ➤ TSX Transfer Stack pointer to X register ➤ TXA Transfer X register to Accumulator ➤ TXS Transfer X register to Stack pointer ➤ TYA Transfer Y register to Accumulator

Slide 30

Slide 30 text

Assembler (ca65) LDA #$8024 A9 24 80 (object file)

Slide 31

Slide 31 text

Linker (ld65) header.o main.o joypad.o graphics.chr game.nes

Slide 32

Slide 32 text

WHY WOULD ANYONE DO THIS?

Slide 33

Slide 33 text

DONKEY KONG (FAMICOM, 1983) Programmer: Course Designer: Original Game Designer: Music & Sound Effects: Producer: Toshihiko Nakago Kenta Usui Shigeru Miyamoto Yukio Taneoka Masayuki Uemura

Slide 34

Slide 34 text

SUPER MARIO BROS. (FAMICOM, 1985) Producer/Director/Designer: Assistant Director/Designer: Sound & Music: Programmer: Programmer: Shigeru Miyamoto Takashi Tezuka Koji Kondo Toshihiko Nakago Kazuaki Morita

Slide 35

Slide 35 text

BATTLE KID: FORTRESS OF PERIL (NES, 2010) https://www.youtube.com/watch?v=yne04hukuyc

Slide 36

Slide 36 text

KIRA KIRA STAR NIGHT DX (FAMICOM, OCTOBER 2016) https://www.youtube.com/watch?v=1UTxXLCQOEk

Slide 37

Slide 37 text

TWIN DRAGONS (NES, DECEMBER 2017) https://www.kickstarter.com/projects/1454702417/twin-dragons-a-brand-new-game-for-the-nes

Slide 38

Slide 38 text

HOW DOES THE NES WORK?

Slide 39

Slide 39 text

+

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

2A03 2C02 https://133fsb.wordpress.com/2009/11/28/restoring-a-nice-famiclone-part-2/

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

CPU
 (6502) Picture Processing Unit
 (PPU) CHR-ROM PRG-ROM NES HARDWARE CARTRIDGE RAM
 (2KB)

Slide 45

Slide 45 text

HOW DOES THE PPU DRAW GRAPHICS ON THE SCREEN?

Slide 46

Slide 46 text

256x240 pixels

Slide 47

Slide 47 text

256x240 pixels = 61,440 pixels

Slide 48

Slide 48 text

256x240 pixels = 61,440 pixels ÷ 4 pixels / byte

Slide 49

Slide 49 text

256x240 pixels = 61,440 pixels ÷ 4 pixels / byte = 15,360 bytes
 per screen

Slide 50

Slide 50 text

256x240 pixels = 61,440 pixels ÷ 4 pixels / byte = 15,360 bytes
 per screen

Slide 51

Slide 51 text

BOOTSTRAPPING GRAPHICS 8x8-pixel "tile"
 as basic unit 1 screen = 32x30 tiles (960 bytes)

Slide 52

Slide 52 text

PATTERN TABLES ("TILESETS") Sprite (foreground) tiles Background tiles

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

PALETTES or: “Where Do Colors Come From?”

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text

No content

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

1 Background / Transparency color
 + 24 additional colors (3 colors per palette x 8 palettes) at any given time

Slide 60

Slide 60 text

NAME TABLES or:
 “How do you make pretty backgrounds?”

Slide 61

Slide 61 text

NAME TABLES Screen 1 ($2000) Screen 2 ($2400) Screen 3 ($2800) Screen 4 ($2C00)

Slide 62

Slide 62 text

¥9800 (~$40)

Slide 63

Slide 63 text

VERTICAL MIRRORING (HORIZONTAL SCROLLING) Image by 
 Damian Yerrick
 (tepples)

Slide 64

Slide 64 text

HORIZONTAL MIRRORING (VERTICAL SCROLLING) Image by 
 Damian Yerrick
 (tepples)

Slide 65

Slide 65 text

$24 $24 $24 $24 $24 $24 $24 $24 $24 $24 $24 $24 $16 $0A $1B $12 $18 $24 $24 $24 $24 $24 $24 $24 $00 $00 $04 $02 $05 $00 $24 $24 $2E $29 $00 $05 $24 $24 $24 $24 $24 $24 $24 $24 $24 $24 $24 $24

Slide 66

Slide 66 text

ATTRIBUTE TABLES or: “How do we assign colors to backgrounds?”

Slide 67

Slide 67 text

Name table (960 bytes) Attribute table (64 bytes)

Slide 68

Slide 68 text

%01100011 Bits 0-1, top left: %11 = palette 3 Bits 2-3, top right: %00 = palette 0 Bits 4-5, bottom left: %10 = palette 2 Bits 6-7, bottom right: %01 = palette 1

Slide 69

Slide 69 text

metatiles

Slide 70

Slide 70 text

metatiles

Slide 71

Slide 71 text

SNAKE RATTLE ’N ROLL (NES, 1990)

Slide 72

Slide 72 text

OBJECT ACCESS MEMORY (OAM) or: “What about things that move around?”

Slide 73

Slide 73 text

CPU
 (6502) Picture Processing Unit
 (PPU) RAM
 (2KB) OAM (256 bytes)

Slide 74

Slide 74 text

256 bytes OAM ÷ 4 bytes / sprite ——————— 64 sprites (max)

Slide 75

Slide 75 text

No content

Slide 76

Slide 76 text

FOUR BYTES PER SPRITE ➤ Y position of top-left corner (0-255) ➤ Tile number (0-255) ➤ Attributes
 (palette, flip vertical/horizontal, priority) ➤ X position of top-left corner (0-255)

Slide 77

Slide 77 text

No content

Slide 78

Slide 78 text

No content

Slide 79

Slide 79 text

REGISTERS ACCUMULATOR (A) INDEX REGISTER (X) 8 BITS 8 BITS 8 BITS INDEX REGISTER (Y) Temporary storage / can do math Temporary storage / used to make loops

Slide 80

Slide 80 text

OTHER IMPORTANT HARDWARE ADDRESS BUS PROGRAM COUNTER (PC) 16 BITS 16 BITS

Slide 81

Slide 81 text

CPU MEMORY MAP (64KB) $0000 $FFFF $8000 INTERRUPT
 VECTORS PRG-ROM FROM CARTRIDGE
 (CODE YOU WRITE) ZEROPAGE
 RAM STACK OAM (SPRITE)
 BUFFER EMPTY SPACE, MEMORY-MAPPED
 I/O SAVE/
 WORK
 RAM
 (OPTIONAL) $6000 $0200 $0100 $FFFA

Slide 82

Slide 82 text

SOME ASSEMBLY REQUIRED

Slide 83

Slide 83 text

LOADING AND STORING DATA LDA $8000 Load Accumulator with data stored at $8000 LDA #$80 Load Accumulator with actual hex value 80

Slide 84

Slide 84 text

LOADING AND STORING DATA LDX #%01101001 Load X register with binary value 01101001 LDY #32 Load Y register with the decimal value 32

Slide 85

Slide 85 text

LOADING AND STORING DATA STX $A0 Store value of X register at address $A0
 (in fast zeropage RAM) STA $0200,x Store Accumulator value at $0200 + value of X
 (“indexed addressing”)

Slide 86

Slide 86 text

INCREMENTING AND DECREMENTING INX Increment X register INY Increment Y register DEX Decrement X register DEY Decrement Y register

Slide 87

Slide 87 text

BEQ label BRANCHING, OR: HOW ASSEMBLY DOES IF/THEN Branch to label if last result equals zero Branch to label if last result did not equal zero BNE label

Slide 88

Slide 88 text

A FULL EXAMPLE: ZEROING OUT ZERO-PAGE RAM LDA #$00 LDX #$00 clear_zeropage: STA $00,x INX BNE clear_zeropage

Slide 89

Slide 89 text

INTERRUPT VECTORS or:
 "What Happens When You Turn It On?"

Slide 90

Slide 90 text

No content

Slide 91

Slide 91 text

$FFFA $FFFB $FFFC $FFFD $FFFE $FFFF { { { Address of
 NMI
 handler Address of
 RES
 handler Address of
 IRQ
 handler

Slide 92

Slide 92 text

RESET (RES) VECTOR Initializes the system at power-on / reset: ➤ Set all RAM to zero ➤ Turn on PPU ➤ Set up APU (audio)

Slide 93

Slide 93 text

BREAK (IRQ) VECTOR Runs when you use the BRK command You will probably never use it

Slide 94

Slide 94 text

NON-MASKABLE INTERRUPT (NMI) VECTOR Runs once per frame, during VBlank

Slide 95

Slide 95 text

WHAT IS “VBLANK”?

Slide 96

Slide 96 text

http://www.circuitstoday.com/crt-cathode-ray-tube

Slide 97

Slide 97 text

No content

Slide 98

Slide 98 text

VBlank

Slide 99

Slide 99 text

NON-MASKABLE INTERRUPT (NMI) VECTOR Runs once per frame, during VBlank IT WILL BE YOUR BEST FRIEND

Slide 100

Slide 100 text

Game loop NMI handler mainloop: ; play music ; do physics ; manage state JMP mainloop nmi_handler: ; read controllers ; update sprites ; re-draw screen RTI +

Slide 101

Slide 101 text

DEBUGGING or:
 "It's not working! What happened?"

Slide 102

Slide 102 text

DEBUGGING WITH FCEUX (WINDOWS VERSION VIA WINE)

Slide 103

Slide 103 text

ADVANCED TOPICS

Slide 104

Slide 104 text

MAP/LEVEL STORAGE

Slide 105

Slide 105 text

No content

Slide 106

Slide 106 text

14 screens (name tables) x 960 bytes / screen 13,440 bytes

Slide 107

Slide 107 text

14 screens (name tables) x 960 bytes / screen 13,440 bytes x 32 stages 430,080 bytes (!!!)

Slide 108

Slide 108 text

"set decoration"
 (3 name tables wide) "I AM ERROR", p.128

Slide 109

Slide 109 text

No content

Slide 110

Slide 110 text

;level 1–1 
 L_GroundArea6: .db $50, $21
 .db $07, $81, $47, $24, $57, $00, $63, $01, $77, $01
 .db $c9, $71, $68, $f2, $e7, $73, $97, $fb, $06, $83 
 .db $5c, $01, $d7, $22, $e7, $00, $03, $a7, $6c, $02 
 .db $b3, $22, $e3, $01, $e7, $07, $47, $a0, $57, $06 
 .db $a7, $01, $d3, $00, $d7, $01, $07, $81, $67, $20 
 .db $93, $22, $03, $a3, $1c, $61, $17, $21, $6f, $33 
 .db $c7, $63, $d8, $62, $e9, $61, $fa, $60, $4f, $b3 
 .db $87, $63, $9c, $01, $b7, $63, $c8, $62, $d9, $61 
 .db $ea, $60, $39, $f1, $87, $21, $a7, $01, $b7, $20 
 .db $39, $f1, $5f, $38, $6d, $c1, $af, $26
 .db $fd 101 bytes "I AM ERROR", p.131

Slide 111

Slide 111 text

;level 1–1 
 L_GroundArea6: .db $50, $21
 .db $07, $81, $47, $24, $57, $00, $63, $01, $77, $01
 .db $c9, $71, $68, $f2, $e7, $73, $97, $fb, $06, $83 
 .db $5c, $01, $d7, $22, $e7, $00, $03, $a7, $6c, $02 
 .db $b3, $22, $e3, $01, $e7, $07, $47, $a0, $57, $06 
 .db $a7, $01, $d3, $00, $d7, $01, $07, $81, $67, $20 
 .db $93, $22, $03, $a3, $1c, $61, $17, $21, $6f, $33 
 .db $c7, $63, $d8, $62, $e9, $61, $fa, $60, $4f, $b3 
 .db $87, $63, $9c, $01, $b7, $63, $c8, $62, $d9, $61 
 .db $ea, $60, $39, $f1, $87, $21, $a7, $01, $b7, $20 
 .db $39, $f1, $5f, $38, $6d, $c1, $af, $26
 .db $fd 101 bytes Header Footer "I AM ERROR", p.131

Slide 112

Slide 112 text

;level 1–1 
 L_GroundArea6: .db $50, $21
 .db $07, $81, $47, $24, $57, $00, $63, $01, $77, $01
 .db $c9, $71, $68, $f2, $e7, $73, $97, $fb, $06, $83 
 .db $5c, $01, $d7, $22, $e7, $00, $03, $a7, $6c, $02 
 .db $b3, $22, $e3, $01, $e7, $07, $47, $a0, $57, $06 
 .db $a7, $01, $d3, $00, $d7, $01, $07, $81, $67, $20 
 .db $93, $22, $03, $a3, $1c, $61, $17, $21, $6f, $33 
 .db $c7, $63, $d8, $62, $e9, $61, $fa, $60, $4f, $b3 
 .db $87, $63, $9c, $01, $b7, $63, $c8, $62, $d9, $61 
 .db $ea, $60, $39, $f1, $87, $21, $a7, $01, $b7, $20 
 .db $39, $f1, $5f, $38, $6d, $c1, $af, $26
 .db $fd 101 bytes Header Footer Byte pairs (screen location, metatile) "I AM ERROR", p.131

Slide 113

Slide 113 text

Byte pair Column/Row Description $07, $81 0, 7 (new page) small obj "?" block w/ coin $47, $24 4, 7 large obj row of bricks, length 4 $57, $00 5, 7 small obj "?" block w/ power-up item $63, $01 6, 3 small obj "?" block w/ coin "I AM ERROR", p.133

Slide 114

Slide 114 text

3 name tables x 960 bytes / name table —————————— 2,880 bytes (reused across multiple levels) + 101 bytes (world 1-1 object data) —————————— 2,981 bytes

Slide 115

Slide 115 text

RANDOM NUMBERS

Slide 116

Slide 116 text

No content

Slide 117

Slide 117 text

THE NES HAS NO BUILT-IN RANDOM NUMBER GENERATOR. !

Slide 118

Slide 118 text

NASIR GEBELLI http://allincolorforaquarter.blogspot.ca/2015/08/nasir-gebelli-and-early-days-of-sirius.html

Slide 119

Slide 119 text

AE D0 38 8A ED 60 DB 72 5C 59 27 D8 0A 4A F4 34 08 A9 C3 96 56 3B F1 55 F8 6B 31 EF 6D 28 AC 41 68 1E 2A C1 E5 8F 50 F5 3E 7B B7 4C 14 39 12 CD B2 62 8B 82 3C BA 63 85 3A 17 B8 2E B5 BE 20 CB 46 51 2C CF 03 78 53 97 06 69 EB 77 86 E6 EA 74 0C 21 E2 40 D4 5A 3D C7 2B 94 D5 8C 44 FD EE D2 43 00 BB FA C6 1D 98 A0 D3 54 5F 5E DC A8 00 AF 93 A1 E1 6C 04 DE B6 D7 36 16 C5 C8 C4 E4 0F 02 AB E8 33 99 73 11 6A 09 67 F3 FF A2 DF 32 0E 1F 0D 90 25 64 75 B3 65 2F C9 B0 DA 5D 9F EC 29 CE E3 F0 91 7A 58 45 24 1C 47 A4 89 18 2D CC BD 6F 80 F6 81 22 E9 07 70 FB DD AD 35 A6 61 B4 A3 FE B1 30 4B 15 48 6E 4F 5B 13 9C 83 92 01 C2 19 7F 1A 1B 71 B9 3F 4E 9B BF 9E 87 0B 10 57 F2 26 79 9A 05 C0 E0 F7 4D 7D CA 52 9D F9 BC AA FC 8D 7E D1 A5 42 E7 D6 76 A7 84 8E 66 7C 23 88 37 49 D9

Slide 120

Slide 120 text

COMPLEX HIT DETECTION

Slide 121

Slide 121 text

No content

Slide 122

Slide 122 text

point x > rect left && point x < rect right && point y > rect top && point y < rect bottom FAST COLLISION DETECTION - POINT VS. RECTANGLE .

Slide 123

Slide 123 text

No content

Slide 124

Slide 124 text

No content

Slide 125

Slide 125 text

Image by Allan Blomquist, http://tomorrowcorporation.com/posts/retro-game-internals-contra-collision-detection

Slide 126

Slide 126 text

BUT WAIT!
 THERE’S MORE!

Slide 127

Slide 127 text

THINGS WE DIDN’T TALK ABOUT ➤ Reading the controllers ➤ Audio (music, sound effects) ➤ Hardware scrolling ➤ Mapper chips (MMC1, MMC3, etc.) & bank switching ➤ Alternative input devices (Zapper, etc.) ➤ Battery-backed save RAM ➤ “Sprite 0 hit” / raster effects

Slide 128

Slide 128 text

FURTHER RESOURCES or: "It's dangerous to go alone! Take this!"

Slide 129

Slide 129 text

https://github.com/kzurawel/selfconf2017

Slide 130

Slide 130 text

BOOKS ➤ Altice, Nathan. “I AM ERROR”. MIT Press, 2015. ➤ Zaks, Rodnay. "Programming the 6502". Sybex, 1978. ➤ Mansfield, Richard. "Machine Language for Beginners". Compute! Publications, 1983.
 (available at http://www.atariarchives.org/mlb/) ➤ Zaks, Rodnay. "Advanced 6502 Programming". Sybex, 1982.

Slide 131

Slide 131 text

WEBSITES ➤ TEXTFILES, http://web.textfiles.com/games/ ➤ PinEight (NROM template and more),
 https://pineight.com/nes/ ➤ http://nesdev.com (wiki and forums) ➤ http://www.6502.org and http://visual6502.org ➤ NintendoAge - The Brewery forum,
 http://nintendoage.com/forum/categories.cfm?catid=22

Slide 132

Slide 132 text

TOOLS ➤ cc65 compiler suite (ca65 assembler and ld65 linker):
 http://cc65.github.io/cc65/ ➤ NES Screen Tool:
 https://shiru.untergrund.net/software.shtml ➤ FCEUX (w/ debugger on Windows):
 http://www.fceux.com/web/download.html ➤ Nestopia (cross-platform emulator):
 http://nestopia.sourceforge.net/

Slide 133

Slide 133 text

GAME-SPECIFIC RESOURCES ➤ “A Comprehensive Super Mario Bros. Disassembly”, 
 https://gist.github.com/1wErt3r/4048722 ➤ “TASVideos: Final Fantasy”,
 http://tasvideos.org/GameResources/NES/FinalFantasy1.html ➤ “Retro Internals: Contra”,
 http://tomorrowcorporation.com/posts/retro-game-internals ➤“Programming M.C. Kids”,
 http://games.greggman.com/game/programming_m_c__kids/

Slide 134

Slide 134 text

BIT.LY/SELFCONF-NES-ASSEMBLY TWITTER: @KZURAWEL EMAIL: [email protected]