diff --git a/src/main/fragment/mos6502-common/vbuaa=pbuz1_derefidx_vbuyy_minus_vbuaa.asm b/src/main/fragment/mos6502-common/vbuaa=pbuz1_derefidx_vbuyy_minus_vbuaa.asm new file mode 100644 index 000000000..65816a958 --- /dev/null +++ b/src/main/fragment/mos6502-common/vbuaa=pbuz1_derefidx_vbuyy_minus_vbuaa.asm @@ -0,0 +1,3 @@ +eor #$ff +sec +adc ({z1}),y diff --git a/src/main/kc/include/nes.h b/src/main/kc/include/nes.h new file mode 100644 index 000000000..e7473ca9f --- /dev/null +++ b/src/main/kc/include/nes.h @@ -0,0 +1,29 @@ +// Nintendo Entertainment System (NES +// https://en.wikipedia.org/wiki/Nintendo_Entertainment_System_(Model_NES-101) +// https://github.com/gregkrsak/first_nes +#include +#include + +// NES Picture Processing Unit (PPU) +struct RICOH_2C02 * PPU = 0x2000; + +// NES CPU and audion processing unit (APU) +struct RICOH_2A03 * APU = 0x4000; + +// Pointer to the start of RAM memory +char * const MEMORY = 0; + +// Disable audio output +void disableAudioOutput(); + +// Disable video output. This will cause a black screen and disable vblank. +void disableVideoOutput(); + +// Enable video output. This will enable vblank. +void enableVideoOutput(); + +// Wait for vblank to start +void waitForVBlank(); + +// Clear the vblank flag +void clearVBlankFlag(); diff --git a/src/main/kc/include/ricoh_2a03.h b/src/main/kc/include/ricoh_2a03.h new file mode 100644 index 000000000..4a212242d --- /dev/null +++ b/src/main/kc/include/ricoh_2a03.h @@ -0,0 +1,123 @@ +// Ricoh 2A03 Nintendo Entertainment System CPU and audio processing unit (APU) +// Ricoh 2A03 or RP2A03 (NTSC version) / Ricoh 2A07 or RP2A07 (PAL version) +// https://en.wikipedia.org/wiki/Ricoh_2A03 +// https://www.nesdev.com/2A03%20technical%20reference.txt +// https://wiki.nesdev.com/w/index.php/2A03 +// https://wiki.nesdev.com/w/index.php/APU +// Based on: https://github.com/gregkrsak/first_nes written by Greg M. Krsak, 2018. + + +// The APU (Audio Processing Unit) is the sound hardware the NES console which generates sound. +struct RICOH_2A03 { + // APU Square wave channels 1 and 2 + // Reference: https://wiki.nesdev.com/w/index.php/APU_Pulse + // Duty and volume for square wave 1 + // $4000 SQ1_VOL Duty and volume for square wave 1 + char SQ1_VOL; + // $4001 SQ1_SWEEP Sweep control register for square wave 1 + char SQ1_SWEEP; + // $4002 SQ1_LO Low byte of period for square wave 1 + char SQ1_LO; + // $4003 SQ1_HI High byte of period and length counter value for square wave 1 + char SQ1_HI; + // $4004 SQ2_VOL Duty and volume for square wave 2 + char SQ2_VOL; + // $4005 SQ2_SWEEP Sweep control register for square wave 2 + char SQ2_SWEEP; + // $4006 SQ2_LO Low byte of period for square wave 2 + char SQ2_LO; + // $4007 SQ2_HI High byte of period and length counter value for square wave 2 + char SQ2_HI; + // APU Triangle wave channel + // Reference: https://wiki.nesdev.com/w/index.php/APU_Triangle + // $4008 TRI_LINEAR Triangle wave linear counter + char TRI_LINEAR; + // $4009 Unused, but is eventually accessed in memory-clearing loops + char UNUSED1; + // $400A TRI_LO Low byte of period for triangle wave + char TRI_LO; + // $400B TRI_HI High byte of period and length counter value for triangle wave + char TRI_HI; + // APU Noise generator + // Reference: https://wiki.nesdev.com/w/index.php/APU_Noise + // $400C NOISE_VOL Volume for noise generator + char NOISE_VOL; + // $400D Unused, but is eventually accessed in memory-clearing loops + char UNUSED2; + // $400E NOISE_LO Period and waveform shape for noise generator + char NOISE_LO; + // $400F NOISE_HI Length counter value for noise generator + char NOISE_HI; + // APU Delta Modulation Channel + // Reference: https://wiki.nesdev.com/w/index.php/APU_DMC + // ------+-----+--------------------------------------------------------------- + // $4010 | W | DMC_FREQ Play mode FLAGS and frequency for DMC samples + // ------+-----+--------------------------------------------------------------- + // | 7 | IRQ enabled flag. If clear, the interrupt flag is cleared. + // | 6 | Loop flag + // | 3-0 | Rate index + // ------+-----+--------------------------------------------------------------- + // Rate $0 $1 $2 $3 $4 $5 $6 $7 $8 $9 $A $B $C $D $E $F + // ------------------------------------------------------------------------------ + // NTSC 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54 + // PAL 398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50 + // + // The rate determines for how many CPU cycles happen between changes in the output level during automatic delta-encoded sample playback. + // For example, on NTSC (1.789773 MHz), a rate of 428 gives a frequency of 1789773/428 Hz = 4181.71 Hz. + // These periods are all even numbers because there are 2 CPU cycles in an APU cycle. A rate of 428 means the output level changes every 214 APU cycles. + char DMC_FREQ; + // $4011 DMC_RAW 7-bit DAC + char DMC_RAW; + // $4012 DMC_START Start of DMC waveform is at address $C000 + $40*$xx + char DMC_START; + // $4013 DMC_LEN Length of DMC waveform is $10*$xx + 1 bytes (128*$xx + 8 samples) + char DMC_LEN; + // $4014 OAMDMA Writing $xx copies 256 bytes by reading from $xx00-$xxFF and writing to OAMDATA ($2004). The CPU is suspended while the transfer is taking place. + // Reference: https://wiki.nesdev.com/w/index.php/PPU_registers#OAMDMA + char OAMDMA; + // ------+-----+--------------------------------------------------------------- + // $4015 | W | Sound Channel Switch + // | 0 | Channel 1, 1 = enable sound. + // | 1 | Channel 2, 1 = enable sound. + // | 2 | Channel 3, 1 = enable sound. + // | 3 | Channel 4, 1 = enable sound. + // | 4 | Channel 5, 1 = enable sound. + // | 5-7 | Unused (???) + // ------+-----+--------------------------------------------------------------- + // $4015 SND_CHN Sound channels enable and status + // Reference: https://wiki.nesdev.com/w/index.php/APU#Status_.28.244015.29 + char SND_CHN; + // ------+-----+--------------------------------------------------------------- + // $4016 | W | JOY1 Joystick 1 data (R) and joystick strobe (W) + // | 0 | Controller port latch bit + // | 1-2 | Expansion port latch bits + // ------+-----+--------------------------------------------------------------- + // $4016 | R | JOY1 Joystick 1 data (R) and joystick strobe (W) + // | 0-4 | Input data lines /D4 D3 D2 D1 D0) controller port 1 + // ------+-----+--------------------------------------------------------------- + // https://wiki.nesdev.com/w/index.php/Input_devices + // https://wiki.nesdev.com/w/index.php/Controller_reading + char JOY1; + // ------+-----+--------------------------------------------------------------- + // $4017 | R | JOY2 Joystick 2 data (R) and frame counter control (W) + // | 0-4 | Input data lines /D4 D3 D2 D1 D0) controller port 2 + // ------+-----+--------------------------------------------------------------- + char JOY2; +}; + +// APU Frame Counter +// generates low-frequency clocks for the channels and an optional 60 Hz interrupt. +// https://wiki.nesdev.com/w/index.php/APU_Frame_Counter +// ------+-----+--------------------------------------------------------------- +// $4017 | W | FR_COUNTER Frame Counter Set mode and interrupt +// ------+-----+--------------------------------------------------------------- +// | 7 | Sequencer mode: 0 selects 4-step sequence, 1 selects 5-step sequence +// | 6 | Interrupt inhibit flag. If set, the frame interrupt flag is cleared, otherwise it is unaffected. +// ------+-----+--------------------------------------------------------------- +// Side effects After 3 or 4 CPU clock cycles*, the timer is reset. +// If the mode flag is set, then both "quarter frame" and "half frame" signals are also generated. +char * const FR_COUNTER = 0x4017; + + + + diff --git a/src/main/kc/include/ricoh_2c02.h b/src/main/kc/include/ricoh_2c02.h new file mode 100644 index 000000000..c0266d877 --- /dev/null +++ b/src/main/kc/include/ricoh_2c02.h @@ -0,0 +1,132 @@ +// Ricoh 2C02 - NES Picture Processing Unit (PPU) +// Ricoh RP2C02 (NTSC version) / RP2C07 (PAL version), +// https://en.wikipedia.org/wiki/Picture_Processing_Unit +// https://wiki.nesdev.com/w/index.php/PPU_registers +// http://nesdev.com/2C02%20technical%20reference.TXT +// Based on: https://github.com/gregkrsak/first_nes written by Greg M. Krsak, 2018. + +// PPU Memory Map +// $0000-$0FFF $1000 Pattern table 0 +char * const PPU_PATTERN_TABLE_0 = 0x0000; +// $1000-$1FFF $1000 Pattern table 1 +char * const PPU_PATTERN_TABLE_1 = 0x1000; +// $2000-$23FF $0400 Nametable 0 +char * const PPU_NAME_TABLE_0 = 0x2000; +// $2400-$27FF $0400 Nametable 1 +char * const PPU_NAME_TABLE_1 = 0x2400; +// $2800-$2BFF $0400 Nametable 2 +char * const PPU_NAME_TABLE_2 = 0x2800; +// $2C00-$2FFF $0400 Nametable 3 +char * const PPU_NAME_TABLE_3 = 0x2c00; +// $3000-$3EFF $0F00 Mirrors of $2000-$2EFF +// $3F00-$3F1F $0020 Palette RAM indexes +char * const PPU_PALETTE = 0x3f00; +// $3F20-$3FFF $00E0 Mirrors of $3F00-$3F1F + +struct RICOH_2C02 { + // ------+-----+--------------------------------------------------------------- + // $2000 | RW | PPU Control Register 1 + // | 0-1 | Name Table Address: + // | | + // | | +-----------+-----------+ + // | | | 2 ($2800) | 3 ($2C00) | + // | | +-----------+-----------+ + // | | | 0 ($2000) | 1 ($2400) | + // | | +-----------+-----------+ + // | | + // | | Remember that because of the mirroring there are only 2 + // | | real Name Tables, not 4. Also, PPU will automatically + // | | switch to another Name Table when running off the current + // | | Name Table during scroll (see picture above). + // | 2 | Vertical Write, 1 = PPU memory address increments by 32: + // | | + // | | Name Table, VW=0 Name Table, VW=1 + // | | +----------------+ +----------------+ + // | | |----> write | | | write | + // | | | | | V | + // | | + // | 3 | Sprite Pattern Table Address, 1 = $1000, 0 = $0000. + // | 4 | Screen Pattern Table Address, 1 = $1000, 0 = $0000. + // | 5 | Sprite Size, 1 = 8x16, 0 = 8x8. + // | 6 | PPU Master/Slave Mode, not used in NES. + // | 7 | VBlank Enable, 1 = generate interrupts on VBlank. + // ------+-----+--------------------------------------------------------------- + char PPUCTRL; // Reference: https://wiki.nesdev.com/w/index.php/PPU_registers#PPUCTRL + // ------+-----+--------------------------------------------------------------- + // $2001 | RW | PPU Control Register 2 + // | 0 | Unknown (???) + // | 1 | Image Mask, 0 = don't show left 8 columns of the screen. + // | 2 | Sprite Mask, 0 = don't show sprites in left 8 columns. + // | 3 | Screen Enable, 1 = show picture, 0 = blank screen. + // | 4 | Sprites Enable, 1 = show sprites, 0 = hide sprites. + // | 5-7 | Background Color, 0 = black, 1 = blue, 2 = green, 4 = red. + // | | Do not use any other numbers as you may damage PPU hardware. + // ------+-----+--------------------------------------------------------------- + char PPUMASK; // Reference: https://wiki.nesdev.com/w/index.php/PPU_registers#PPUMASK + // ------+-----+--------------------------------------------------------------- + // $2002 | R | PPU Status Register + // | 0-5 | Unknown (???) + // | 6 | Hit Flag, 1 = Sprite refresh has hit sprite #0. + // | | This flag resets to 0 when screen refresh starts. + // | 7 | VBlank Flag, 1 = PPU is in VBlank state. + // | | This flag resets to 0 when VBlank ends or CPU reads $2002. + // ------+-----+--------------------------------------------------------------- + volatile char PPUSTATUS; // Reference: https://wiki.nesdev.com/w/index.php/PPU_registers#PPUSTATUS + // The OAM (Object Attribute Memory) is internal memory inside the PPU that contains a lookup table + // of up to 64 sprites, where each table entry consists of 4 bytes. + // ------+-----+--------------------------------------------------------------- + // $2003 | W | Sprite Memory Address + // | | Used to set the address of the 256-byte Sprite Memory to be + // | | accessed via $2004. This address will increment by 1 after + // | | each access to $2004. Sprite Memory contains coordinates, + // | | colors, and other sprite attributes. + // ------+-----+--------------------------------------------------------------- + char OAMADDR; // Reference: https://wiki.nesdev.com/w/index.php/PPU_registers#OAMADDR + // ------+-----+--------------------------------------------------------------- + // $2004 | RW | Sprite Memory Data + // | | Used to read/write the Sprite Memory. The address is set via + // | | $2003 and increments by 1 after each access. Sprite Memory + // | | contains coordinates, colors, and other sprite attributes + // | | sprites. + // ------+-----+--------------------------------------------------------------- + char OAMDATA; // Reference: https://wiki.nesdev.com/w/index.php/PPU_registers#OAMDATA + // ------+-----+--------------------------------------------------------------- + // $2005 | W | Screen Scroll Offsets + // | | There are two scroll registers, vertical and horizontal, + // | | which are both written via this port. The first value written + // | | will go into the Vertical Scroll Register (unless it is >239, + // | | then it will be ignored). The second value will appear in the + // | | Horizontal Scroll Register. Name Tables are assumed to be + // | | arranged in the following way: + // | | + // | | +-----------+-----------+ + // | | | 2 ($2800) | 3 ($2C00) | + // | | +-----------+-----------+ + // | | | 0 ($2000) | 1 ($2400) | + // | | +-----------+-----------+ + // | | + // | | When scrolled, the picture may span over several Name Tables. + // | | Remember that because of the mirroring there are only 2 real + // | | Name Tables, not 4. + // ------+-----+--------------------------------------------------------------- + char PPUSCROLL; // Reference: https://wiki.nesdev.com/w/index.php/PPU_registers#PPUSCROLL + // ------+-----+--------------------------------------------------------------- + // $2006 | W | PPU Memory Address + // | | Used to set the address of PPU Memory to be accessed via + // | | $2007. The first write to this register will set 8 lower + // | | address bits. The second write will set 6 upper bits. The + // | | address will increment either by 1 or by 32 after each + // | | access to $2007. + // ------+-----+--------------------------------------------------------------- + char PPUADDR; // Reference: https://wiki.nesdev.com/w/index.php/PPU_registers#PPUADDR + // ------+-----+--------------------------------------------------------------- + // $2007 | RW | PPU Memory Data + // | | Used to read/write the PPU Memory. The address is set via + // | | $2006 and increments after each access, either by 1 or by 32. + // ------+-----+--------------------------------------------------------------- + char PPUDATA; // Reference: https://wiki.nesdev.com/w/index.php/PPU_registers#PPUDATA +}; + +// PPU Status Register for reading in ASM +volatile char * PPU_PPUSTATUS = 0x2002; + diff --git a/src/main/kc/lib/nes.c b/src/main/kc/lib/nes.c new file mode 100644 index 000000000..d053b3ace --- /dev/null +++ b/src/main/kc/lib/nes.c @@ -0,0 +1,38 @@ +// Nintendo Entertainment System (NES +// https://en.wikipedia.org/wiki/Nintendo_Entertainment_System_(Model_NES-101) +// https://github.com/gregkrsak/first_nes +#include + +// Disable audio output +inline void disableAudioOutput() { + // Disable APU frame IRQ + *FR_COUNTER = 0b01000000; + // Disable digital sound IRQs + APU->DMC_FREQ = 0b01000000; +} + +// Disable video output. This will cause a black screen and disable vblank. +inline void disableVideoOutput() { + // Disable vertical blank interrupt + PPU->PPUCTRL = 0; + // Disable sprite rendering + PPU->PPUMASK = 0; +} + +// Enable video output. This will enable vblank. +inline void enableVideoOutput() { + // Enable vertical blank interrupt + PPU->PPUCTRL = 0b10000000; + // Enable sprite rendering + PPU->PPUMASK = 0b00010000; +} + +// Wait for vblank to start +inline void waitForVBlank() { + while(!(PPU->PPUSTATUS&0x80)) { } +} + +// Clear the vblank flag +inline void clearVBlankFlag() { + asm { lda PPU_PPUSTATUS } +} diff --git a/src/main/kc/target/nes.ld b/src/main/kc/target/nes.ld new file mode 100644 index 000000000..63da9dcf0 --- /dev/null +++ b/src/main/kc/target/nes.ld @@ -0,0 +1,27 @@ +// Nintendo Entertainment System (NES) ROM +// https://sadistech.com/nesromtool/romdoc.html +// https://forums.nesdev.com/viewtopic.php?f=2&t=9896 +// https://github.com/gregkrsak/first_nes +.file [name="%O", type="bin", segments="NesRom"] +.file [name="%O_hdr", type="bin", segments="Header"] +.file [name="%O_prg", type="bin", segments="ProgramRom"] +.file [name="%O_chr", type="bin", segments="CharacterRom"] +.segmentdef Header [ start=$0000, min=$0000, max=$000f, fill ] +.segmentdef Tiles [ start=$0000, min=$0000, max=$1fff, fill ] +.segmentdef Code [ start=$c000, min=$c000, max=$fff9 ] +.segmentdef Data [ startAfter="Code", min=$c000, max=$fff9 ] +.segmentdef Vectors [ start=$fffa, min=$fffa, max=$ffff ] +.segmentdef ProgramRom [ segments="Code, Data, Vectors" ] +.segmentdef CharacterRom [ segments="Tiles" ] +.segmentdef NesRom +//.segment NesRom +//.segmentout [ segments="Header" ] +//.segmentout [ segments="ProgramRom" ] +//.segmentout [ segments="CharacterRom" ] +.segment Header +.text @"NES\$1a" +.byte $01 // 1x 16KB ROM (PRG) +.byte $01 // 1x 8KB VROM (CHR) +.byte %00000001 // Mapper nibble 0000 == No mapping (a simple 16KB PRG + 8KB CHR game) + // Mirroring nibble 0001 == Vertical mirroring only +.segment Code diff --git a/src/main/kc/target/nes.tgt b/src/main/kc/target/nes.tgt new file mode 100644 index 000000000..a2e8cda9c --- /dev/null +++ b/src/main/kc/target/nes.tgt @@ -0,0 +1,10 @@ +{ + "extension": "nes", + "link": "nes.ld", + "cpu": "MOS6502", + "emulator": "nestopia", + "zp_reserve": [ ], + "defines": { + "__NES__": 1 + } +} \ No newline at end of file diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 99ad2ae53..7d309f04e 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -44,6 +44,11 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testNesDemo() throws IOException, URISyntaxException { + compileAndCompare("examples/nes/nes-demo.c"); + } + @Test public void testAtari2600Sprites() throws IOException, URISyntaxException { compileAndCompare("examples/atari2600/atari2600-sprites.c"); diff --git a/src/test/kc/examples/nes/nes-demo.c b/src/test/kc/examples/nes/nes-demo.c new file mode 100644 index 000000000..f29a64af9 --- /dev/null +++ b/src/test/kc/examples/nes/nes-demo.c @@ -0,0 +1,159 @@ +// A minimal NES demo +// Based on: https://github.com/gregkrsak/first_nes written by Greg M. Krsak, 2018. +#pragma target(nes) +#include +#include + +// RESET Called when the NES is reset, including when it is turned on. +void main() { + asm { + cld + ldx #$ff + txs + } + + // Initialize the video & audio + disableVideoOutput(); + disableAudioOutput(); + // Note: When the system is first turned on or reset, the PPU may not be in a usable state right + // away. You should wait at least 30,000 (thirty thousand) CPU cycles for the PPU to initialize, + // which may be accomplished by waiting for 2 (two) vertical blank intervals. + clearVBlankFlag(); + waitForVBlank(); + // Clear RAM - since it has all variables and the stack it is necesary to do it inline + char i=0; + do { + (MEMORY+0x000)[i] = 0; + (MEMORY+0x100)[i] = 0; + (MEMORY+0x200)[i] = 0; + (MEMORY+0x300)[i] = 0; + (MEMORY+0x400)[i] = 0; + (MEMORY+0x500)[i] = 0; + (MEMORY+0x600)[i] = 0; + (MEMORY+0x700)[i] = 0; + } while (++i); + waitForVBlank(); + // Now the PPU is ready. + + initPaletteData(); + initSpriteData(); + enableVideoOutput(); + + // Infinite loop + while(1) { + } + +} + +// NMI Called when the PPU refreshes the screen (also known as the V-Blank period) +interrupt(hardware_stack) void vblank() { + + // Refresh DRAM-stored sprite data before it decays. + // Set OAM start address to sprite#0 + PPU->OAMADDR = 0; + // Set the high byte (02) of the RAM address and start the DMA transfer to OAM memory + APU->OAMDMA = >OAM_BUFFER; + + // Freeze the button positions. + APU->JOY1 = 1; + APU->JOY1 = 0; + // Controllers for first and second player are now latched and will not change + + // Read button A on controller 1 + if(APU->JOY1&0b00000001) { + moveLuigiRight(); + } + + // Read button B on controller 1 + if(APU->JOY1&0b00000001) { + moveLuigiLeft(); + } + +} + +// move the Luigi sprites right +void moveLuigiRight() { + OAM_BUFFER[0x03]++; + OAM_BUFFER[0x07]++; + OAM_BUFFER[0x0b]++; + OAM_BUFFER[0x0f]++; +} + +// move the Luigi sprites left +void moveLuigiLeft() { + OAM_BUFFER[0x03]--; + OAM_BUFFER[0x07]--; + OAM_BUFFER[0x0b]--; + OAM_BUFFER[0x0f]--; +} + +// Copy palette values to PPU +void initPaletteData() { + // Reset the high/low latch to "high" + asm { lda PPU_PPUSTATUS } + // Write the high byte of PPU Palette address + PPU->PPUADDR = >PPU_PALETTE; + // Write the low byte of PPU Palette address + PPU->PPUADDR = PPUDATA = PALETTE[i]; +} + +// OAM (Object Attribute Memory) Buffer +// Will be transfered to the PPU via DMA +char * const OAM_BUFFER = 0x0200; + +// Initialize OAM (Object Attribute Memory) Buffer +void initSpriteData() { + for(char i=0;iPPUCTRL = 0 + lda #0 + sta PPU + // PPU->PPUMASK = 0 + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUMASK + // *FR_COUNTER = 0b01000000 + lda #$40 + sta FR_COUNTER + // APU->DMC_FREQ = 0b01000000 + sta APU+OFFSET_STRUCT_RICOH_2A03_DMC_FREQ + // asm + lda PPU_PPUSTATUS + waitForVBlank1: + // PPU->PPUSTATUS&0x80 + lda #$80 + and PPU+OFFSET_STRUCT_RICOH_2C02_PPUSTATUS + // while(!(PPU->PPUSTATUS&0x80)) + cmp #0 + beq waitForVBlank1 + ldx #0 + __b1: + // (MEMORY+0x000)[i] = 0 + lda #0 + sta MEMORY,x + // (MEMORY+0x100)[i] = 0 + sta MEMORY+$100,x + // (MEMORY+0x200)[i] = 0 + sta MEMORY+$200,x + // (MEMORY+0x300)[i] = 0 + sta MEMORY+$300,x + // (MEMORY+0x400)[i] = 0 + sta MEMORY+$400,x + // (MEMORY+0x500)[i] = 0 + sta MEMORY+$500,x + // (MEMORY+0x600)[i] = 0 + sta MEMORY+$600,x + // (MEMORY+0x700)[i] = 0 + sta MEMORY+$700,x + // while (++i) + inx + cpx #0 + bne __b1 + waitForVBlank2: + // PPU->PPUSTATUS&0x80 + lda #$80 + and PPU+OFFSET_STRUCT_RICOH_2C02_PPUSTATUS + // while(!(PPU->PPUSTATUS&0x80)) + cmp #0 + beq waitForVBlank2 + // initPaletteData() + // Now the PPU is ready. + jsr initPaletteData + // initSpriteData() + jsr initSpriteData + // PPU->PPUCTRL = 0b10000000 + lda #$80 + sta PPU + // PPU->PPUMASK = 0b00010000 + lda #$10 + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUMASK + __b2: + // Infinite loop + jmp __b2 +} +// Initialize OAM (Object Attribute Memory) Buffer +initSpriteData: { + ldx #0 + __b1: + // for(char i=0;iPPUADDR = >PPU_PALETTE + // Write the high byte of PPU Palette address + lda #>PPU_PALETTE + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUADDR + // PPU->PPUADDR = PPUDATA = PALETTE[i] + lda PALETTE,x + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUDATA + // for(char i=0;iOAMADDR = 0 + // Refresh DRAM-stored sprite data before it decays. + // Set OAM start address to sprite#0 + lda #0 + sta PPU+OFFSET_STRUCT_RICOH_2C02_OAMADDR + // APU->OAMDMA = >OAM_BUFFER + // Set the high byte (02) of the RAM address and start the DMA transfer to OAM memory + lda #>OAM_BUFFER + sta APU+OFFSET_STRUCT_RICOH_2A03_OAMDMA + // APU->JOY1 = 1 + // Freeze the button positions. + lda #1 + sta APU+OFFSET_STRUCT_RICOH_2A03_JOY1 + // APU->JOY1 = 0 + lda #0 + sta APU+OFFSET_STRUCT_RICOH_2A03_JOY1 + // APU->JOY1&0b00000001 + lda #1 + and APU+OFFSET_STRUCT_RICOH_2A03_JOY1 + // if(APU->JOY1&0b00000001) + cmp #0 + beq __b1 + // moveLuigiRight() + jsr moveLuigiRight + __b1: + // APU->JOY1&0b00000001 + lda #1 + and APU+OFFSET_STRUCT_RICOH_2A03_JOY1 + // if(APU->JOY1&0b00000001) + cmp #0 + beq __breturn + // moveLuigiLeft() + jsr moveLuigiLeft + __breturn: + // } + pla + tay + pla + tax + pla + rti +} +// move the Luigi sprites left +moveLuigiLeft: { + // OAM_BUFFER[0x03]--; + dec OAM_BUFFER+3 + // OAM_BUFFER[0x07]--; + dec OAM_BUFFER+7 + // OAM_BUFFER[0x0b]--; + dec OAM_BUFFER+$b + // OAM_BUFFER[0x0f]--; + dec OAM_BUFFER+$f + // } + rts +} +// move the Luigi sprites right +moveLuigiRight: { + // OAM_BUFFER[0x03]++; + inc OAM_BUFFER+3 + // OAM_BUFFER[0x07]++; + inc OAM_BUFFER+7 + // OAM_BUFFER[0x0b]++; + inc OAM_BUFFER+$b + // OAM_BUFFER[0x0f]++; + inc OAM_BUFFER+$f + // } + rts +} +.segment Data + PALETTE: .byte $f, $31, $32, $33, $f, $35, $36, $37, $f, $39, $3a, $3b, $f, $3d, $3e, $f, $f, $1c, $15, $14, $f, 2, $38, $3c, $f, $30, $37, $1a, $f, $f, $f, $f + // Small Luigi Sprite Data + SPRITES: .byte $80, $36, 2, $80, $80, $37, 2, $88, $88, $38, 2, $80, $88, $39, 2, $88 +.segment Tiles +TILES: +.import binary "smb1_chr.bin" + +.segment Vectors + VECTORS: .word vblank, main, 0 +.segment NesRom +NES_ROM: +.segmentout [ segments="Header" ] +.segmentout [ segments="ProgramRom" ] +.segmentout [ segments="CharacterRom" ] + diff --git a/src/test/ref/examples/nes/nes-demo.cfg b/src/test/ref/examples/nes/nes-demo.cfg new file mode 100644 index 000000000..3e70a3b6d --- /dev/null +++ b/src/test/ref/examples/nes/nes-demo.cfg @@ -0,0 +1,148 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() + +(void()) main() +main: scope:[main] from @1 + asm { cld ldx#$ff txs } + to:main::disableVideoOutput1 +main::disableVideoOutput1: scope:[main] from main + [5] *((byte*)(const struct RICOH_2C02*) PPU) ← (byte) 0 + [6] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK) ← (byte) 0 + to:main::disableAudioOutput1 +main::disableAudioOutput1: scope:[main] from main::disableVideoOutput1 + [7] *((const nomodify byte*) FR_COUNTER) ← (byte) $40 + [8] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_DMC_FREQ) ← (byte) $40 + to:main::clearVBlankFlag1 +main::clearVBlankFlag1: scope:[main] from main::disableAudioOutput1 + asm { ldaPPU_PPUSTATUS } + to:main::waitForVBlank1 +main::waitForVBlank1: scope:[main] from main::clearVBlankFlag1 + [10] phi() + to:main::waitForVBlank1_@1 +main::waitForVBlank1_@1: scope:[main] from main::waitForVBlank1 main::waitForVBlank1_@1 + [11] (byte~) main::waitForVBlank1_$0 ← *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) & (byte) $80 + [12] if((byte) 0==(byte~) main::waitForVBlank1_$0) goto main::waitForVBlank1_@1 + to:main::@1 +main::@1: scope:[main] from main::@1 main::waitForVBlank1_@1 + [13] (byte) main::i#2 ← phi( main::@1/(byte) main::i#1 main::waitForVBlank1_@1/(byte) 0 ) + [14] *((const nomodify byte*) MEMORY + (byte) main::i#2) ← (byte) 0 + [15] *((const nomodify byte*) MEMORY+(word) $100 + (byte) main::i#2) ← (byte) 0 + [16] *((const nomodify byte*) MEMORY+(word) $200 + (byte) main::i#2) ← (byte) 0 + [17] *((const nomodify byte*) MEMORY+(word) $300 + (byte) main::i#2) ← (byte) 0 + [18] *((const nomodify byte*) MEMORY+(word) $400 + (byte) main::i#2) ← (byte) 0 + [19] *((const nomodify byte*) MEMORY+(word) $500 + (byte) main::i#2) ← (byte) 0 + [20] *((const nomodify byte*) MEMORY+(word) $600 + (byte) main::i#2) ← (byte) 0 + [21] *((const nomodify byte*) MEMORY+(word) $700 + (byte) main::i#2) ← (byte) 0 + [22] (byte) main::i#1 ← ++ (byte) main::i#2 + [23] if((byte) 0!=(byte) main::i#1) goto main::@1 + to:main::waitForVBlank2 +main::waitForVBlank2: scope:[main] from main::@1 + [24] phi() + to:main::waitForVBlank2_@1 +main::waitForVBlank2_@1: scope:[main] from main::waitForVBlank2 main::waitForVBlank2_@1 + [25] (byte~) main::waitForVBlank2_$0 ← *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) & (byte) $80 + [26] if((byte) 0==(byte~) main::waitForVBlank2_$0) goto main::waitForVBlank2_@1 + to:main::@3 +main::@3: scope:[main] from main::waitForVBlank2_@1 + [27] phi() + [28] call initPaletteData + to:main::@4 +main::@4: scope:[main] from main::@3 + [29] phi() + [30] call initSpriteData + to:main::enableVideoOutput1 +main::enableVideoOutput1: scope:[main] from main::@4 + [31] *((byte*)(const struct RICOH_2C02*) PPU) ← (byte) $80 + [32] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK) ← (byte) $10 + to:main::@2 +main::@2: scope:[main] from main::@2 main::enableVideoOutput1 + [33] phi() + to:main::@2 + +(void()) initSpriteData() +initSpriteData: scope:[initSpriteData] from main::@4 + [34] phi() + to:initSpriteData::@1 +initSpriteData::@1: scope:[initSpriteData] from initSpriteData initSpriteData::@2 + [35] (byte) initSpriteData::i#2 ← phi( initSpriteData/(byte) 0 initSpriteData::@2/(byte) initSpriteData::i#1 ) + [36] if((byte) initSpriteData::i#2<(byte) $10*(const byte) SIZEOF_BYTE) goto initSpriteData::@2 + to:initSpriteData::@return +initSpriteData::@return: scope:[initSpriteData] from initSpriteData::@1 + [37] return + to:@return +initSpriteData::@2: scope:[initSpriteData] from initSpriteData::@1 + [38] *((const nomodify byte*) OAM_BUFFER + (byte) initSpriteData::i#2) ← *((const byte*) SPRITES + (byte) initSpriteData::i#2) + [39] (byte) initSpriteData::i#1 ← ++ (byte) initSpriteData::i#2 + to:initSpriteData::@1 + +(void()) initPaletteData() +initPaletteData: scope:[initPaletteData] from main::@3 + asm { ldaPPU_PPUSTATUS } + [41] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR) ← >(const nomodify byte*) PPU_PALETTE + [42] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR) ← (byte) 0 + to:initPaletteData::@1 +initPaletteData::@1: scope:[initPaletteData] from initPaletteData initPaletteData::@2 + [43] (byte) initPaletteData::i#2 ← phi( initPaletteData/(byte) 0 initPaletteData::@2/(byte) initPaletteData::i#1 ) + [44] if((byte) initPaletteData::i#2<(byte) $20*(const byte) SIZEOF_BYTE) goto initPaletteData::@2 + to:initPaletteData::@return +initPaletteData::@return: scope:[initPaletteData] from initPaletteData::@1 + [45] return + to:@return +initPaletteData::@2: scope:[initPaletteData] from initPaletteData::@1 + [46] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUDATA) ← *((const byte*) PALETTE + (byte) initPaletteData::i#2) + [47] (byte) initPaletteData::i#1 ← ++ (byte) initPaletteData::i#2 + to:initPaletteData::@1 + +interrupt(HARDWARE_STACK)(void()) vblank() +vblank: scope:[vblank] from + [48] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_OAMADDR) ← (byte) 0 + [49] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_OAMDMA) ← >(const nomodify byte*) OAM_BUFFER + [50] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) ← (byte) 1 + [51] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) ← (byte) 0 + [52] (byte~) vblank::$1 ← *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) & (byte) 1 + [53] if((byte) 0==(byte~) vblank::$1) goto vblank::@1 + to:vblank::@2 +vblank::@2: scope:[vblank] from vblank + [54] phi() + [55] call moveLuigiRight + to:vblank::@1 +vblank::@1: scope:[vblank] from vblank vblank::@2 + [56] (byte~) vblank::$3 ← *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) & (byte) 1 + [57] if((byte) 0==(byte~) vblank::$3) goto vblank::@return + to:vblank::@3 +vblank::@3: scope:[vblank] from vblank::@1 + [58] phi() + [59] call moveLuigiLeft + to:vblank::@return +vblank::@return: scope:[vblank] from vblank::@1 vblank::@3 + [60] return + to:@return + +(void()) moveLuigiLeft() +moveLuigiLeft: scope:[moveLuigiLeft] from vblank::@3 + [61] *((const nomodify byte*) OAM_BUFFER+(byte) 3) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) 3) + [62] *((const nomodify byte*) OAM_BUFFER+(byte) 7) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) 7) + [63] *((const nomodify byte*) OAM_BUFFER+(byte) $b) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) $b) + [64] *((const nomodify byte*) OAM_BUFFER+(byte) $f) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) $f) + to:moveLuigiLeft::@return +moveLuigiLeft::@return: scope:[moveLuigiLeft] from moveLuigiLeft + [65] return + to:@return + +(void()) moveLuigiRight() +moveLuigiRight: scope:[moveLuigiRight] from vblank::@2 + [66] *((const nomodify byte*) OAM_BUFFER+(byte) 3) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) 3) + [67] *((const nomodify byte*) OAM_BUFFER+(byte) 7) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) 7) + [68] *((const nomodify byte*) OAM_BUFFER+(byte) $b) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) $b) + [69] *((const nomodify byte*) OAM_BUFFER+(byte) $f) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) $f) + to:moveLuigiRight::@return +moveLuigiRight::@return: scope:[moveLuigiRight] from moveLuigiRight + [70] return + to:@return diff --git a/src/test/ref/examples/nes/nes-demo.log b/src/test/ref/examples/nes/nes-demo.log new file mode 100644 index 000000000..a90a04271 --- /dev/null +++ b/src/test/ref/examples/nes/nes-demo.log @@ -0,0 +1,2571 @@ +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference OAM_BUFFER to (const nomodify byte*) OAM_BUFFER +Resolved forward reference PALETTE to (const byte*) PALETTE +Resolved forward reference PALETTE to (const byte*) PALETTE +Resolved forward reference SPRITES to (const byte*) SPRITES +Resolved forward reference SPRITES to (const byte*) SPRITES +Inlined call call disableVideoOutput +Inlined call call disableAudioOutput +Inlined call call clearVBlankFlag +Inlined call call waitForVBlank +Inlined call call waitForVBlank +Inlined call call enableVideoOutput + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@1 + +(void()) main() +main: scope:[main] from @1 + asm { cld ldx#$ff txs } + to:main::disableVideoOutput1 +main::disableVideoOutput1: scope:[main] from main + (byte*~) main::disableVideoOutput1_$2 ← (byte*)(const struct RICOH_2C02*) PPU + (byte*~) main::disableVideoOutput1_$0 ← (byte*~) main::disableVideoOutput1_$2 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUCTRL + *((byte*~) main::disableVideoOutput1_$0) ← (number) 0 + (byte*~) main::disableVideoOutput1_$3 ← (byte*)(const struct RICOH_2C02*) PPU + (byte*~) main::disableVideoOutput1_$1 ← (byte*~) main::disableVideoOutput1_$3 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK + *((byte*~) main::disableVideoOutput1_$1) ← (number) 0 + to:main::disableAudioOutput1 +main::disableAudioOutput1: scope:[main] from main::disableVideoOutput1 + *((const nomodify byte*) FR_COUNTER) ← (number) $40 + (byte*~) main::disableAudioOutput1_$1 ← (byte*)(const struct RICOH_2A03*) APU + (byte*~) main::disableAudioOutput1_$0 ← (byte*~) main::disableAudioOutput1_$1 + (const byte) OFFSET_STRUCT_RICOH_2A03_DMC_FREQ + *((byte*~) main::disableAudioOutput1_$0) ← (number) $40 + to:main::clearVBlankFlag1 +main::clearVBlankFlag1: scope:[main] from main::disableAudioOutput1 + asm { ldaPPU_PPUSTATUS } + to:main::waitForVBlank1 +main::waitForVBlank1: scope:[main] from main::clearVBlankFlag1 + to:main::waitForVBlank1_@1 +main::waitForVBlank1_@1: scope:[main] from main::waitForVBlank1 main::waitForVBlank1_@1 + (byte*~) main::waitForVBlank1_$3 ← (byte*)(const struct RICOH_2C02*) PPU + (byte*~) main::waitForVBlank1_$2 ← (byte*~) main::waitForVBlank1_$3 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS + (number~) main::waitForVBlank1_$0 ← *((byte*~) main::waitForVBlank1_$2) & (number) $80 + (bool~) main::waitForVBlank1_$4 ← (number) 0 != (number~) main::waitForVBlank1_$0 + (bool~) main::waitForVBlank1_$1 ← ! (bool~) main::waitForVBlank1_$4 + if((bool~) main::waitForVBlank1_$1) goto main::waitForVBlank1_@1 + to:main::@3 +main::@3: scope:[main] from main::waitForVBlank1_@1 + (byte) main::i#0 ← (byte) 0 + to:main::@1 +main::@1: scope:[main] from main::@1 main::@3 + (byte) main::i#2 ← phi( main::@1/(byte) main::i#1 main::@3/(byte) main::i#0 ) + *((const nomodify byte*) MEMORY+(number) 0 + (byte) main::i#2) ← (number) 0 + *((const nomodify byte*) MEMORY+(number) $100 + (byte) main::i#2) ← (number) 0 + *((const nomodify byte*) MEMORY+(number) $200 + (byte) main::i#2) ← (number) 0 + *((const nomodify byte*) MEMORY+(number) $300 + (byte) main::i#2) ← (number) 0 + *((const nomodify byte*) MEMORY+(number) $400 + (byte) main::i#2) ← (number) 0 + *((const nomodify byte*) MEMORY+(number) $500 + (byte) main::i#2) ← (number) 0 + *((const nomodify byte*) MEMORY+(number) $600 + (byte) main::i#2) ← (number) 0 + *((const nomodify byte*) MEMORY+(number) $700 + (byte) main::i#2) ← (number) 0 + (byte) main::i#1 ← ++ (byte) main::i#2 + (bool~) main::$8 ← (number) 0 != (byte) main::i#1 + if((bool~) main::$8) goto main::@1 + to:main::waitForVBlank2 +main::waitForVBlank2: scope:[main] from main::@1 + to:main::waitForVBlank2_@1 +main::waitForVBlank2_@1: scope:[main] from main::waitForVBlank2 main::waitForVBlank2_@1 + (byte*~) main::waitForVBlank2_$3 ← (byte*)(const struct RICOH_2C02*) PPU + (byte*~) main::waitForVBlank2_$2 ← (byte*~) main::waitForVBlank2_$3 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS + (number~) main::waitForVBlank2_$0 ← *((byte*~) main::waitForVBlank2_$2) & (number) $80 + (bool~) main::waitForVBlank2_$4 ← (number) 0 != (number~) main::waitForVBlank2_$0 + (bool~) main::waitForVBlank2_$1 ← ! (bool~) main::waitForVBlank2_$4 + if((bool~) main::waitForVBlank2_$1) goto main::waitForVBlank2_@1 + to:main::@4 +main::@4: scope:[main] from main::waitForVBlank2_@1 + call initPaletteData + to:main::@5 +main::@5: scope:[main] from main::@4 + call initSpriteData + to:main::@6 +main::@6: scope:[main] from main::@5 + to:main::enableVideoOutput1 +main::enableVideoOutput1: scope:[main] from main::@6 + (byte*~) main::enableVideoOutput1_$2 ← (byte*)(const struct RICOH_2C02*) PPU + (byte*~) main::enableVideoOutput1_$0 ← (byte*~) main::enableVideoOutput1_$2 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUCTRL + *((byte*~) main::enableVideoOutput1_$0) ← (number) $80 + (byte*~) main::enableVideoOutput1_$3 ← (byte*)(const struct RICOH_2C02*) PPU + (byte*~) main::enableVideoOutput1_$1 ← (byte*~) main::enableVideoOutput1_$3 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK + *((byte*~) main::enableVideoOutput1_$1) ← (number) $10 + to:main::@2 +main::@2: scope:[main] from main::@2 main::enableVideoOutput1 + (bool~) main::$9 ← (number) 0 != (number) 1 + if((bool~) main::$9) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@2 + return + to:@return + +interrupt(HARDWARE_STACK)(void()) vblank() +vblank: scope:[vblank] from + (byte*~) vblank::$13 ← (byte*)(const struct RICOH_2C02*) PPU + (byte*~) vblank::$7 ← (byte*~) vblank::$13 + (const byte) OFFSET_STRUCT_RICOH_2C02_OAMADDR + *((byte*~) vblank::$7) ← (number) 0 + (byte~) vblank::$0 ← > (const nomodify byte*) OAM_BUFFER + (byte*~) vblank::$14 ← (byte*)(const struct RICOH_2A03*) APU + (byte*~) vblank::$8 ← (byte*~) vblank::$14 + (const byte) OFFSET_STRUCT_RICOH_2A03_OAMDMA + *((byte*~) vblank::$8) ← (byte~) vblank::$0 + (byte*~) vblank::$15 ← (byte*)(const struct RICOH_2A03*) APU + (byte*~) vblank::$9 ← (byte*~) vblank::$15 + (const byte) OFFSET_STRUCT_RICOH_2A03_JOY1 + *((byte*~) vblank::$9) ← (number) 1 + (byte*~) vblank::$16 ← (byte*)(const struct RICOH_2A03*) APU + (byte*~) vblank::$10 ← (byte*~) vblank::$16 + (const byte) OFFSET_STRUCT_RICOH_2A03_JOY1 + *((byte*~) vblank::$10) ← (number) 0 + (byte*~) vblank::$17 ← (byte*)(const struct RICOH_2A03*) APU + (byte*~) vblank::$11 ← (byte*~) vblank::$17 + (const byte) OFFSET_STRUCT_RICOH_2A03_JOY1 + (number~) vblank::$1 ← *((byte*~) vblank::$11) & (number) 1 + (bool~) vblank::$19 ← (number) 0 != (number~) vblank::$1 + (bool~) vblank::$2 ← ! (bool~) vblank::$19 + if((bool~) vblank::$2) goto vblank::@1 + to:vblank::@2 +vblank::@1: scope:[vblank] from vblank vblank::@4 + (byte*~) vblank::$18 ← (byte*)(const struct RICOH_2A03*) APU + (byte*~) vblank::$12 ← (byte*~) vblank::$18 + (const byte) OFFSET_STRUCT_RICOH_2A03_JOY1 + (number~) vblank::$3 ← *((byte*~) vblank::$12) & (number) 1 + (bool~) vblank::$20 ← (number) 0 != (number~) vblank::$3 + (bool~) vblank::$4 ← ! (bool~) vblank::$20 + if((bool~) vblank::$4) goto vblank::@return + to:vblank::@3 +vblank::@2: scope:[vblank] from vblank + call moveLuigiRight + to:vblank::@4 +vblank::@4: scope:[vblank] from vblank::@2 + to:vblank::@1 +vblank::@3: scope:[vblank] from vblank::@1 + call moveLuigiLeft + to:vblank::@5 +vblank::@5: scope:[vblank] from vblank::@3 + to:vblank::@return +vblank::@return: scope:[vblank] from vblank::@1 vblank::@5 + return + to:@return + +(void()) moveLuigiRight() +moveLuigiRight: scope:[moveLuigiRight] from vblank::@2 + *((const nomodify byte*) OAM_BUFFER + (number) 3) ← ++ *((const nomodify byte*) OAM_BUFFER + (number) 3) + *((const nomodify byte*) OAM_BUFFER + (number) 7) ← ++ *((const nomodify byte*) OAM_BUFFER + (number) 7) + *((const nomodify byte*) OAM_BUFFER + (number) $b) ← ++ *((const nomodify byte*) OAM_BUFFER + (number) $b) + *((const nomodify byte*) OAM_BUFFER + (number) $f) ← ++ *((const nomodify byte*) OAM_BUFFER + (number) $f) + to:moveLuigiRight::@return +moveLuigiRight::@return: scope:[moveLuigiRight] from moveLuigiRight + return + to:@return + +(void()) moveLuigiLeft() +moveLuigiLeft: scope:[moveLuigiLeft] from vblank::@3 + *((const nomodify byte*) OAM_BUFFER + (number) 3) ← -- *((const nomodify byte*) OAM_BUFFER + (number) 3) + *((const nomodify byte*) OAM_BUFFER + (number) 7) ← -- *((const nomodify byte*) OAM_BUFFER + (number) 7) + *((const nomodify byte*) OAM_BUFFER + (number) $b) ← -- *((const nomodify byte*) OAM_BUFFER + (number) $b) + *((const nomodify byte*) OAM_BUFFER + (number) $f) ← -- *((const nomodify byte*) OAM_BUFFER + (number) $f) + to:moveLuigiLeft::@return +moveLuigiLeft::@return: scope:[moveLuigiLeft] from moveLuigiLeft + return + to:@return + +(void()) initPaletteData() +initPaletteData: scope:[initPaletteData] from main::@4 + asm { ldaPPU_PPUSTATUS } + (byte*~) initPaletteData::$5 ← (byte*)(const struct RICOH_2C02*) PPU + (byte*~) initPaletteData::$2 ← (byte*~) initPaletteData::$5 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR + *((byte*~) initPaletteData::$2) ← >(const nomodify byte*) PPU_PALETTE + (byte*~) initPaletteData::$6 ← (byte*)(const struct RICOH_2C02*) PPU + (byte*~) initPaletteData::$3 ← (byte*~) initPaletteData::$6 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR + *((byte*~) initPaletteData::$3) ← <(const nomodify byte*) PPU_PALETTE + (byte) initPaletteData::i#0 ← (byte) 0 + to:initPaletteData::@1 +initPaletteData::@1: scope:[initPaletteData] from initPaletteData initPaletteData::@2 + (byte) initPaletteData::i#2 ← phi( initPaletteData/(byte) initPaletteData::i#0 initPaletteData::@2/(byte) initPaletteData::i#1 ) + (byte~) initPaletteData::$0 ← sizeof (const byte*) PALETTE + (bool~) initPaletteData::$1 ← (byte) initPaletteData::i#2 < (byte~) initPaletteData::$0 + if((bool~) initPaletteData::$1) goto initPaletteData::@2 + to:initPaletteData::@return +initPaletteData::@2: scope:[initPaletteData] from initPaletteData::@1 + (byte) initPaletteData::i#3 ← phi( initPaletteData::@1/(byte) initPaletteData::i#2 ) + (byte*~) initPaletteData::$7 ← (byte*)(const struct RICOH_2C02*) PPU + (byte*~) initPaletteData::$4 ← (byte*~) initPaletteData::$7 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUDATA + *((byte*~) initPaletteData::$4) ← *((const byte*) PALETTE + (byte) initPaletteData::i#3) + (byte) initPaletteData::i#1 ← ++ (byte) initPaletteData::i#3 + to:initPaletteData::@1 +initPaletteData::@return: scope:[initPaletteData] from initPaletteData::@1 + return + to:@return + +(void()) initSpriteData() +initSpriteData: scope:[initSpriteData] from main::@5 + (byte) initSpriteData::i#0 ← (byte) 0 + to:initSpriteData::@1 +initSpriteData::@1: scope:[initSpriteData] from initSpriteData initSpriteData::@2 + (byte) initSpriteData::i#2 ← phi( initSpriteData/(byte) initSpriteData::i#0 initSpriteData::@2/(byte) initSpriteData::i#1 ) + (byte~) initSpriteData::$0 ← sizeof (const byte*) SPRITES + (bool~) initSpriteData::$1 ← (byte) initSpriteData::i#2 < (byte~) initSpriteData::$0 + if((bool~) initSpriteData::$1) goto initSpriteData::@2 + to:initSpriteData::@return +initSpriteData::@2: scope:[initSpriteData] from initSpriteData::@1 + (byte) initSpriteData::i#3 ← phi( initSpriteData::@1/(byte) initSpriteData::i#2 ) + *((const nomodify byte*) OAM_BUFFER + (byte) initSpriteData::i#3) ← *((const byte*) SPRITES + (byte) initSpriteData::i#3) + (byte) initSpriteData::i#1 ← ++ (byte) initSpriteData::i#3 + to:initSpriteData::@1 +initSpriteData::@return: scope:[initSpriteData] from initSpriteData::@1 + return + to:@return +@1: scope:[] from @begin + call main + to:@2 +@2: scope:[] from @1 + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(label) @1 +(label) @2 +(label) @begin +(label) @end +(const struct RICOH_2A03*) APU = (struct RICOH_2A03*)(number) $4000 +(const nomodify byte*) FR_COUNTER = (byte*)(number) $4017 +(const nomodify byte*) MEMORY = (byte*)(number) 0 +(const byte*) NES_ROM[] = kickasm {{ .segmentout [ segments="Header" ] +.segmentout [ segments="ProgramRom" ] +.segmentout [ segments="CharacterRom" ] + }} +(const nomodify byte*) OAM_BUFFER = (byte*)(number) $200 +(const byte) OFFSET_STRUCT_RICOH_2A03_DMC_FREQ = (byte) $10 +(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1 = (byte) $16 +(const byte) OFFSET_STRUCT_RICOH_2A03_OAMDMA = (byte) $14 +(const byte) OFFSET_STRUCT_RICOH_2C02_OAMADDR = (byte) 3 +(const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR = (byte) 6 +(const byte) OFFSET_STRUCT_RICOH_2C02_PPUCTRL = (byte) 0 +(const byte) OFFSET_STRUCT_RICOH_2C02_PPUDATA = (byte) 7 +(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK = (byte) 1 +(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS = (byte) 2 +(const byte*) PALETTE[(number) $20] = { (byte) $f, (byte) $31, (byte) $32, (byte) $33, (byte) $f, (byte) $35, (byte) $36, (byte) $37, (byte) $f, (byte) $39, (byte) $3a, (byte) $3b, (byte) $f, (byte) $3d, (byte) $3e, (byte) $f, (byte) $f, (byte) $1c, (byte) $15, (byte) $14, (byte) $f, (byte) 2, (byte) $38, (byte) $3c, (byte) $f, (byte) $30, (byte) $37, (byte) $1a, (byte) $f, (byte) $f, (byte) $f, (byte) $f } +(const struct RICOH_2C02*) PPU = (struct RICOH_2C02*)(number) $2000 +(const nomodify byte*) PPU_PALETTE = (byte*)(number) $3f00 +(const to_volatile byte*) PPU_PPUSTATUS = (byte*)(number) $2002 +(byte) RICOH_2A03::DMC_FREQ +(byte) RICOH_2A03::DMC_LEN +(byte) RICOH_2A03::DMC_RAW +(byte) RICOH_2A03::DMC_START +(byte) RICOH_2A03::JOY1 +(byte) RICOH_2A03::JOY2 +(byte) RICOH_2A03::NOISE_HI +(byte) RICOH_2A03::NOISE_LO +(byte) RICOH_2A03::NOISE_VOL +(byte) RICOH_2A03::OAMDMA +(byte) RICOH_2A03::SND_CHN +(byte) RICOH_2A03::SQ1_HI +(byte) RICOH_2A03::SQ1_LO +(byte) RICOH_2A03::SQ1_SWEEP +(byte) RICOH_2A03::SQ1_VOL +(byte) RICOH_2A03::SQ2_HI +(byte) RICOH_2A03::SQ2_LO +(byte) RICOH_2A03::SQ2_SWEEP +(byte) RICOH_2A03::SQ2_VOL +(byte) RICOH_2A03::TRI_HI +(byte) RICOH_2A03::TRI_LINEAR +(byte) RICOH_2A03::TRI_LO +(byte) RICOH_2A03::UNUSED1 +(byte) RICOH_2A03::UNUSED2 +(byte) RICOH_2C02::OAMADDR +(byte) RICOH_2C02::OAMDATA +(byte) RICOH_2C02::PPUADDR +(byte) RICOH_2C02::PPUCTRL +(byte) RICOH_2C02::PPUDATA +(byte) RICOH_2C02::PPUMASK +(byte) RICOH_2C02::PPUSCROLL +(volatile byte) RICOH_2C02::PPUSTATUS loadstore +(const byte*) SPRITES[] = { (byte) $80, (byte) $36, (byte) 2, (byte) $80, (byte) $80, (byte) $37, (byte) 2, (byte) $88, (byte) $88, (byte) $38, (byte) 2, (byte) $80, (byte) $88, (byte) $39, (byte) 2, (byte) $88 } +(const byte*) TILES[] = kickasm {{ .import binary "smb1_chr.bin" + }} +(const to_nomodify void()**) VECTORS[] = { &interrupt(HARDWARE_STACK)(void()) vblank(), &(void()) main(), (void()*)(number) 0 } +(void()) initPaletteData() +(byte~) initPaletteData::$0 +(bool~) initPaletteData::$1 +(byte*~) initPaletteData::$2 +(byte*~) initPaletteData::$3 +(byte*~) initPaletteData::$4 +(byte*~) initPaletteData::$5 +(byte*~) initPaletteData::$6 +(byte*~) initPaletteData::$7 +(label) initPaletteData::@1 +(label) initPaletteData::@2 +(label) initPaletteData::@return +(byte) initPaletteData::i +(byte) initPaletteData::i#0 +(byte) initPaletteData::i#1 +(byte) initPaletteData::i#2 +(byte) initPaletteData::i#3 +(void()) initSpriteData() +(byte~) initSpriteData::$0 +(bool~) initSpriteData::$1 +(label) initSpriteData::@1 +(label) initSpriteData::@2 +(label) initSpriteData::@return +(byte) initSpriteData::i +(byte) initSpriteData::i#0 +(byte) initSpriteData::i#1 +(byte) initSpriteData::i#2 +(byte) initSpriteData::i#3 +(void()) main() +(bool~) main::$8 +(bool~) main::$9 +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@5 +(label) main::@6 +(label) main::@return +(label) main::clearVBlankFlag1 +(label) main::disableAudioOutput1 +(byte*~) main::disableAudioOutput1_$0 +(byte*~) main::disableAudioOutput1_$1 +(label) main::disableVideoOutput1 +(byte*~) main::disableVideoOutput1_$0 +(byte*~) main::disableVideoOutput1_$1 +(byte*~) main::disableVideoOutput1_$2 +(byte*~) main::disableVideoOutput1_$3 +(label) main::enableVideoOutput1 +(byte*~) main::enableVideoOutput1_$0 +(byte*~) main::enableVideoOutput1_$1 +(byte*~) main::enableVideoOutput1_$2 +(byte*~) main::enableVideoOutput1_$3 +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(label) main::waitForVBlank1 +(number~) main::waitForVBlank1_$0 +(bool~) main::waitForVBlank1_$1 +(byte*~) main::waitForVBlank1_$2 +(byte*~) main::waitForVBlank1_$3 +(bool~) main::waitForVBlank1_$4 +(label) main::waitForVBlank1_@1 +(label) main::waitForVBlank2 +(number~) main::waitForVBlank2_$0 +(bool~) main::waitForVBlank2_$1 +(byte*~) main::waitForVBlank2_$2 +(byte*~) main::waitForVBlank2_$3 +(bool~) main::waitForVBlank2_$4 +(label) main::waitForVBlank2_@1 +(void()) moveLuigiLeft() +(label) moveLuigiLeft::@return +(void()) moveLuigiRight() +(label) moveLuigiRight::@return +interrupt(HARDWARE_STACK)(void()) vblank() +(byte~) vblank::$0 +(number~) vblank::$1 +(byte*~) vblank::$10 +(byte*~) vblank::$11 +(byte*~) vblank::$12 +(byte*~) vblank::$13 +(byte*~) vblank::$14 +(byte*~) vblank::$15 +(byte*~) vblank::$16 +(byte*~) vblank::$17 +(byte*~) vblank::$18 +(bool~) vblank::$19 +(bool~) vblank::$2 +(bool~) vblank::$20 +(number~) vblank::$3 +(bool~) vblank::$4 +(byte*~) vblank::$7 +(byte*~) vblank::$8 +(byte*~) vblank::$9 +(label) vblank::@1 +(label) vblank::@2 +(label) vblank::@3 +(label) vblank::@4 +(label) vblank::@5 +(label) vblank::@return + +Adding number conversion cast (unumber) 0 in *((byte*~) main::disableVideoOutput1_$0) ← (number) 0 +Adding number conversion cast (unumber) 0 in *((byte*~) main::disableVideoOutput1_$1) ← (number) 0 +Adding number conversion cast (unumber) $40 in *((const nomodify byte*) FR_COUNTER) ← (number) $40 +Adding number conversion cast (unumber) $40 in *((byte*~) main::disableAudioOutput1_$0) ← (number) $40 +Adding number conversion cast (unumber) $80 in (number~) main::waitForVBlank1_$0 ← *((byte*~) main::waitForVBlank1_$2) & (number) $80 +Adding number conversion cast (unumber) main::waitForVBlank1_$0 in (number~) main::waitForVBlank1_$0 ← *((byte*~) main::waitForVBlank1_$2) & (unumber)(number) $80 +Adding number conversion cast (unumber) 0 in (bool~) main::waitForVBlank1_$4 ← (number) 0 != (unumber~) main::waitForVBlank1_$0 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) MEMORY+(number) 0 + (byte) main::i#2) ← (number) 0 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) MEMORY+(number) 0 + (byte) main::i#2) ← ((unumber)) (number) 0 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) MEMORY+(number) $100 + (byte) main::i#2) ← (number) 0 +Adding number conversion cast (unumber) $100 in *((const nomodify byte*) MEMORY+(number) $100 + (byte) main::i#2) ← ((unumber)) (number) 0 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) MEMORY+(number) $200 + (byte) main::i#2) ← (number) 0 +Adding number conversion cast (unumber) $200 in *((const nomodify byte*) MEMORY+(number) $200 + (byte) main::i#2) ← ((unumber)) (number) 0 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) MEMORY+(number) $300 + (byte) main::i#2) ← (number) 0 +Adding number conversion cast (unumber) $300 in *((const nomodify byte*) MEMORY+(number) $300 + (byte) main::i#2) ← ((unumber)) (number) 0 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) MEMORY+(number) $400 + (byte) main::i#2) ← (number) 0 +Adding number conversion cast (unumber) $400 in *((const nomodify byte*) MEMORY+(number) $400 + (byte) main::i#2) ← ((unumber)) (number) 0 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) MEMORY+(number) $500 + (byte) main::i#2) ← (number) 0 +Adding number conversion cast (unumber) $500 in *((const nomodify byte*) MEMORY+(number) $500 + (byte) main::i#2) ← ((unumber)) (number) 0 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) MEMORY+(number) $600 + (byte) main::i#2) ← (number) 0 +Adding number conversion cast (unumber) $600 in *((const nomodify byte*) MEMORY+(number) $600 + (byte) main::i#2) ← ((unumber)) (number) 0 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) MEMORY+(number) $700 + (byte) main::i#2) ← (number) 0 +Adding number conversion cast (unumber) $700 in *((const nomodify byte*) MEMORY+(number) $700 + (byte) main::i#2) ← ((unumber)) (number) 0 +Adding number conversion cast (unumber) 0 in (bool~) main::$8 ← (number) 0 != (byte) main::i#1 +Adding number conversion cast (unumber) $80 in (number~) main::waitForVBlank2_$0 ← *((byte*~) main::waitForVBlank2_$2) & (number) $80 +Adding number conversion cast (unumber) main::waitForVBlank2_$0 in (number~) main::waitForVBlank2_$0 ← *((byte*~) main::waitForVBlank2_$2) & (unumber)(number) $80 +Adding number conversion cast (unumber) 0 in (bool~) main::waitForVBlank2_$4 ← (number) 0 != (unumber~) main::waitForVBlank2_$0 +Adding number conversion cast (unumber) $80 in *((byte*~) main::enableVideoOutput1_$0) ← (number) $80 +Adding number conversion cast (unumber) $10 in *((byte*~) main::enableVideoOutput1_$1) ← (number) $10 +Adding number conversion cast (unumber) 0 in *((byte*~) vblank::$7) ← (number) 0 +Adding number conversion cast (unumber) 1 in *((byte*~) vblank::$9) ← (number) 1 +Adding number conversion cast (unumber) 0 in *((byte*~) vblank::$10) ← (number) 0 +Adding number conversion cast (unumber) 1 in (number~) vblank::$1 ← *((byte*~) vblank::$11) & (number) 1 +Adding number conversion cast (unumber) vblank::$1 in (number~) vblank::$1 ← *((byte*~) vblank::$11) & (unumber)(number) 1 +Adding number conversion cast (unumber) 0 in (bool~) vblank::$19 ← (number) 0 != (unumber~) vblank::$1 +Adding number conversion cast (unumber) 1 in (number~) vblank::$3 ← *((byte*~) vblank::$12) & (number) 1 +Adding number conversion cast (unumber) vblank::$3 in (number~) vblank::$3 ← *((byte*~) vblank::$12) & (unumber)(number) 1 +Adding number conversion cast (unumber) 0 in (bool~) vblank::$20 ← (number) 0 != (unumber~) vblank::$3 +Adding number conversion cast (unumber) 3 in *((const nomodify byte*) OAM_BUFFER + (number) 3) ← ++ *((const nomodify byte*) OAM_BUFFER + (number) 3) +Adding number conversion cast (unumber) 3 in *((const nomodify byte*) OAM_BUFFER + (number) 3) ← ++ *((const nomodify byte*) OAM_BUFFER + (unumber)(number) 3) +Adding number conversion cast (unumber) 7 in *((const nomodify byte*) OAM_BUFFER + (number) 7) ← ++ *((const nomodify byte*) OAM_BUFFER + (number) 7) +Adding number conversion cast (unumber) 7 in *((const nomodify byte*) OAM_BUFFER + (number) 7) ← ++ *((const nomodify byte*) OAM_BUFFER + (unumber)(number) 7) +Adding number conversion cast (unumber) $b in *((const nomodify byte*) OAM_BUFFER + (number) $b) ← ++ *((const nomodify byte*) OAM_BUFFER + (number) $b) +Adding number conversion cast (unumber) $b in *((const nomodify byte*) OAM_BUFFER + (number) $b) ← ++ *((const nomodify byte*) OAM_BUFFER + (unumber)(number) $b) +Adding number conversion cast (unumber) $f in *((const nomodify byte*) OAM_BUFFER + (number) $f) ← ++ *((const nomodify byte*) OAM_BUFFER + (number) $f) +Adding number conversion cast (unumber) $f in *((const nomodify byte*) OAM_BUFFER + (number) $f) ← ++ *((const nomodify byte*) OAM_BUFFER + (unumber)(number) $f) +Adding number conversion cast (unumber) 3 in *((const nomodify byte*) OAM_BUFFER + (number) 3) ← -- *((const nomodify byte*) OAM_BUFFER + (number) 3) +Adding number conversion cast (unumber) 3 in *((const nomodify byte*) OAM_BUFFER + (number) 3) ← -- *((const nomodify byte*) OAM_BUFFER + (unumber)(number) 3) +Adding number conversion cast (unumber) 7 in *((const nomodify byte*) OAM_BUFFER + (number) 7) ← -- *((const nomodify byte*) OAM_BUFFER + (number) 7) +Adding number conversion cast (unumber) 7 in *((const nomodify byte*) OAM_BUFFER + (number) 7) ← -- *((const nomodify byte*) OAM_BUFFER + (unumber)(number) 7) +Adding number conversion cast (unumber) $b in *((const nomodify byte*) OAM_BUFFER + (number) $b) ← -- *((const nomodify byte*) OAM_BUFFER + (number) $b) +Adding number conversion cast (unumber) $b in *((const nomodify byte*) OAM_BUFFER + (number) $b) ← -- *((const nomodify byte*) OAM_BUFFER + (unumber)(number) $b) +Adding number conversion cast (unumber) $f in *((const nomodify byte*) OAM_BUFFER + (number) $f) ← -- *((const nomodify byte*) OAM_BUFFER + (number) $f) +Adding number conversion cast (unumber) $f in *((const nomodify byte*) OAM_BUFFER + (number) $f) ← -- *((const nomodify byte*) OAM_BUFFER + (unumber)(number) $f) +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast *((byte*~) main::disableVideoOutput1_$0) ← (unumber)(number) 0 +Inlining cast *((byte*~) main::disableVideoOutput1_$1) ← (unumber)(number) 0 +Inlining cast *((const nomodify byte*) FR_COUNTER) ← (unumber)(number) $40 +Inlining cast *((byte*~) main::disableAudioOutput1_$0) ← (unumber)(number) $40 +Inlining cast *((const nomodify byte*) MEMORY+(unumber)(number) 0 + (byte) main::i#2) ← (unumber)(number) 0 +Inlining cast *((const nomodify byte*) MEMORY+(unumber)(number) $100 + (byte) main::i#2) ← (unumber)(number) 0 +Inlining cast *((const nomodify byte*) MEMORY+(unumber)(number) $200 + (byte) main::i#2) ← (unumber)(number) 0 +Inlining cast *((const nomodify byte*) MEMORY+(unumber)(number) $300 + (byte) main::i#2) ← (unumber)(number) 0 +Inlining cast *((const nomodify byte*) MEMORY+(unumber)(number) $400 + (byte) main::i#2) ← (unumber)(number) 0 +Inlining cast *((const nomodify byte*) MEMORY+(unumber)(number) $500 + (byte) main::i#2) ← (unumber)(number) 0 +Inlining cast *((const nomodify byte*) MEMORY+(unumber)(number) $600 + (byte) main::i#2) ← (unumber)(number) 0 +Inlining cast *((const nomodify byte*) MEMORY+(unumber)(number) $700 + (byte) main::i#2) ← (unumber)(number) 0 +Inlining cast *((byte*~) main::enableVideoOutput1_$0) ← (unumber)(number) $80 +Inlining cast *((byte*~) main::enableVideoOutput1_$1) ← (unumber)(number) $10 +Inlining cast *((byte*~) vblank::$7) ← (unumber)(number) 0 +Inlining cast *((byte*~) vblank::$9) ← (unumber)(number) 1 +Inlining cast *((byte*~) vblank::$10) ← (unumber)(number) 0 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 16128 +Simplifying constant pointer cast (byte*) 16407 +Simplifying constant pointer cast (byte*) 0 +Simplifying constant pointer cast (byte*) 512 +Simplifying constant pointer cast (void()*) 0 +Simplifying constant pointer cast (byte*) 8194 +Simplifying constant pointer cast (struct RICOH_2C02*) 8192 +Simplifying constant pointer cast (struct RICOH_2A03*) 16384 +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Simplifying constant integer cast $40 +Simplifying constant integer cast $40 +Simplifying constant integer cast $80 +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Simplifying constant integer cast $100 +Simplifying constant integer cast 0 +Simplifying constant integer cast $200 +Simplifying constant integer cast 0 +Simplifying constant integer cast $300 +Simplifying constant integer cast 0 +Simplifying constant integer cast $400 +Simplifying constant integer cast 0 +Simplifying constant integer cast $500 +Simplifying constant integer cast 0 +Simplifying constant integer cast $600 +Simplifying constant integer cast 0 +Simplifying constant integer cast $700 +Simplifying constant integer cast 0 +Simplifying constant integer cast $80 +Simplifying constant integer cast 0 +Simplifying constant integer cast $80 +Simplifying constant integer cast $10 +Simplifying constant integer cast 0 +Simplifying constant integer cast 1 +Simplifying constant integer cast 0 +Simplifying constant integer cast 1 +Simplifying constant integer cast 0 +Simplifying constant integer cast 1 +Simplifying constant integer cast 0 +Simplifying constant integer cast 3 +Simplifying constant integer cast 3 +Simplifying constant integer cast 7 +Simplifying constant integer cast 7 +Simplifying constant integer cast $b +Simplifying constant integer cast $b +Simplifying constant integer cast $f +Simplifying constant integer cast $f +Simplifying constant integer cast 3 +Simplifying constant integer cast 3 +Simplifying constant integer cast 7 +Simplifying constant integer cast 7 +Simplifying constant integer cast $b +Simplifying constant integer cast $b +Simplifying constant integer cast $f +Simplifying constant integer cast $f +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) $40 +Finalized unsigned number type (byte) $40 +Finalized unsigned number type (byte) $80 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (word) $100 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (word) $200 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (word) $300 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (word) $400 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (word) $500 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (word) $600 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (word) $700 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) $80 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) $80 +Finalized unsigned number type (byte) $10 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 1 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 1 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 1 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 3 +Finalized unsigned number type (byte) 3 +Finalized unsigned number type (byte) 7 +Finalized unsigned number type (byte) 7 +Finalized unsigned number type (byte) $b +Finalized unsigned number type (byte) $b +Finalized unsigned number type (byte) $f +Finalized unsigned number type (byte) $f +Finalized unsigned number type (byte) 3 +Finalized unsigned number type (byte) 3 +Finalized unsigned number type (byte) 7 +Finalized unsigned number type (byte) 7 +Finalized unsigned number type (byte) $b +Finalized unsigned number type (byte) $b +Finalized unsigned number type (byte) $f +Finalized unsigned number type (byte) $f +Successful SSA optimization PassNFinalizeNumberTypeConversions +Inferred type updated to byte in (unumber~) main::waitForVBlank1_$0 ← *((byte*~) main::waitForVBlank1_$2) & (byte) $80 +Inferred type updated to byte in (unumber~) main::waitForVBlank2_$0 ← *((byte*~) main::waitForVBlank2_$2) & (byte) $80 +Inferred type updated to byte in (unumber~) vblank::$1 ← *((byte*~) vblank::$11) & (byte) 1 +Inferred type updated to byte in (unumber~) vblank::$3 ← *((byte*~) vblank::$12) & (byte) 1 +Inversing boolean not [16] (bool~) main::waitForVBlank1_$1 ← (byte) 0 == (byte~) main::waitForVBlank1_$0 from [15] (bool~) main::waitForVBlank1_$4 ← (byte) 0 != (byte~) main::waitForVBlank1_$0 +Inversing boolean not [35] (bool~) main::waitForVBlank2_$1 ← (byte) 0 == (byte~) main::waitForVBlank2_$0 from [34] (bool~) main::waitForVBlank2_$4 ← (byte) 0 != (byte~) main::waitForVBlank2_$0 +Inversing boolean not [65] (bool~) vblank::$2 ← (byte) 0 == (byte~) vblank::$1 from [64] (bool~) vblank::$19 ← (byte) 0 != (byte~) vblank::$1 +Inversing boolean not [71] (bool~) vblank::$4 ← (byte) 0 == (byte~) vblank::$3 from [70] (bool~) vblank::$20 ← (byte) 0 != (byte~) vblank::$3 +Successful SSA optimization Pass2UnaryNotSimplification +Alias initPaletteData::i#2 = initPaletteData::i#3 +Alias initSpriteData::i#2 = initSpriteData::i#3 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::waitForVBlank1_$1 [16] if((byte) 0==(byte~) main::waitForVBlank1_$0) goto main::waitForVBlank1_@1 +Simple Condition (bool~) main::$8 [29] if((byte) 0!=(byte) main::i#1) goto main::@1 +Simple Condition (bool~) main::waitForVBlank2_$1 [34] if((byte) 0==(byte~) main::waitForVBlank2_$0) goto main::waitForVBlank2_@1 +Simple Condition (bool~) main::$9 [44] if((number) 0!=(number) 1) goto main::@2 +Simple Condition (bool~) vblank::$2 [63] if((byte) 0==(byte~) vblank::$1) goto vblank::@1 +Simple Condition (bool~) vblank::$4 [68] if((byte) 0==(byte~) vblank::$3) goto vblank::@return +Simple Condition (bool~) initPaletteData::$1 [93] if((byte) initPaletteData::i#2<(byte~) initPaletteData::$0) goto initPaletteData::@2 +Simple Condition (bool~) initSpriteData::$1 [103] if((byte) initSpriteData::i#2<(byte~) initSpriteData::$0) goto initSpriteData::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant right-side identified [1] (byte*~) main::disableVideoOutput1_$2 ← (byte*)(const struct RICOH_2C02*) PPU +Constant right-side identified [4] (byte*~) main::disableVideoOutput1_$3 ← (byte*)(const struct RICOH_2C02*) PPU +Constant right-side identified [8] (byte*~) main::disableAudioOutput1_$1 ← (byte*)(const struct RICOH_2A03*) APU +Constant right-side identified [12] (byte*~) main::waitForVBlank1_$3 ← (byte*)(const struct RICOH_2C02*) PPU +Constant right-side identified [30] (byte*~) main::waitForVBlank2_$3 ← (byte*)(const struct RICOH_2C02*) PPU +Constant right-side identified [37] (byte*~) main::enableVideoOutput1_$2 ← (byte*)(const struct RICOH_2C02*) PPU +Constant right-side identified [40] (byte*~) main::enableVideoOutput1_$3 ← (byte*)(const struct RICOH_2C02*) PPU +Constant right-side identified [46] (byte*~) vblank::$13 ← (byte*)(const struct RICOH_2C02*) PPU +Constant right-side identified [49] (byte~) vblank::$0 ← > (const nomodify byte*) OAM_BUFFER +Constant right-side identified [50] (byte*~) vblank::$14 ← (byte*)(const struct RICOH_2A03*) APU +Constant right-side identified [53] (byte*~) vblank::$15 ← (byte*)(const struct RICOH_2A03*) APU +Constant right-side identified [56] (byte*~) vblank::$16 ← (byte*)(const struct RICOH_2A03*) APU +Constant right-side identified [59] (byte*~) vblank::$17 ← (byte*)(const struct RICOH_2A03*) APU +Constant right-side identified [64] (byte*~) vblank::$18 ← (byte*)(const struct RICOH_2A03*) APU +Constant right-side identified [83] (byte*~) initPaletteData::$5 ← (byte*)(const struct RICOH_2C02*) PPU +Constant right-side identified [86] (byte*~) initPaletteData::$6 ← (byte*)(const struct RICOH_2C02*) PPU +Constant right-side identified [91] (byte~) initPaletteData::$0 ← sizeof (const byte*) PALETTE +Constant right-side identified [94] (byte*~) initPaletteData::$7 ← (byte*)(const struct RICOH_2C02*) PPU +Constant right-side identified [101] (byte~) initSpriteData::$0 ← sizeof (const byte*) SPRITES +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const byte*) main::disableVideoOutput1_$2 = (byte*)PPU +Constant (const byte*) main::disableVideoOutput1_$3 = (byte*)PPU +Constant (const byte*) main::disableAudioOutput1_$1 = (byte*)APU +Constant (const byte*) main::waitForVBlank1_$3 = (byte*)PPU +Constant (const byte) main::i#0 = 0 +Constant (const byte*) main::waitForVBlank2_$3 = (byte*)PPU +Constant (const byte*) main::enableVideoOutput1_$2 = (byte*)PPU +Constant (const byte*) main::enableVideoOutput1_$3 = (byte*)PPU +Constant (const byte*) vblank::$13 = (byte*)PPU +Constant (const byte) vblank::$0 = >OAM_BUFFER +Constant (const byte*) vblank::$14 = (byte*)APU +Constant (const byte*) vblank::$15 = (byte*)APU +Constant (const byte*) vblank::$16 = (byte*)APU +Constant (const byte*) vblank::$17 = (byte*)APU +Constant (const byte*) vblank::$18 = (byte*)APU +Constant (const byte*) initPaletteData::$5 = (byte*)PPU +Constant (const byte*) initPaletteData::$6 = (byte*)PPU +Constant (const byte) initPaletteData::i#0 = 0 +Constant (const byte) initPaletteData::$0 = sizeof PALETTE +Constant (const byte*) initPaletteData::$7 = (byte*)PPU +Constant (const byte) initSpriteData::i#0 = 0 +Constant (const byte) initSpriteData::$0 = sizeof SPRITES +Successful SSA optimization Pass2ConstantIdentification +if() condition always true - replacing block destination [44] if((number) 0!=(number) 1) goto main::@2 +Successful SSA optimization Pass2ConstantIfs +Converting *(pointer+n) to pointer[n] [3] *((byte*~) main::disableVideoOutput1_$0) ← (byte) 0 -- *(main::disableVideoOutput1_$2 + OFFSET_STRUCT_RICOH_2C02_PPUCTRL) +Converting *(pointer+n) to pointer[n] [6] *((byte*~) main::disableVideoOutput1_$1) ← (byte) 0 -- *(main::disableVideoOutput1_$3 + OFFSET_STRUCT_RICOH_2C02_PPUMASK) +Converting *(pointer+n) to pointer[n] [10] *((byte*~) main::disableAudioOutput1_$0) ← (byte) $40 -- *(main::disableAudioOutput1_$1 + OFFSET_STRUCT_RICOH_2A03_DMC_FREQ) +Converting *(pointer+n) to pointer[n] [14] (byte~) main::waitForVBlank1_$0 ← *((byte*~) main::waitForVBlank1_$2) & (byte) $80 -- *(main::waitForVBlank1_$3 + OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) +Converting *(pointer+n) to pointer[n] [32] (byte~) main::waitForVBlank2_$0 ← *((byte*~) main::waitForVBlank2_$2) & (byte) $80 -- *(main::waitForVBlank2_$3 + OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) +Converting *(pointer+n) to pointer[n] [39] *((byte*~) main::enableVideoOutput1_$0) ← (byte) $80 -- *(main::enableVideoOutput1_$2 + OFFSET_STRUCT_RICOH_2C02_PPUCTRL) +Converting *(pointer+n) to pointer[n] [42] *((byte*~) main::enableVideoOutput1_$1) ← (byte) $10 -- *(main::enableVideoOutput1_$3 + OFFSET_STRUCT_RICOH_2C02_PPUMASK) +Converting *(pointer+n) to pointer[n] [48] *((byte*~) vblank::$7) ← (byte) 0 -- *(vblank::$13 + OFFSET_STRUCT_RICOH_2C02_OAMADDR) +Converting *(pointer+n) to pointer[n] [52] *((byte*~) vblank::$8) ← (const byte) vblank::$0 -- *(vblank::$14 + OFFSET_STRUCT_RICOH_2A03_OAMDMA) +Converting *(pointer+n) to pointer[n] [55] *((byte*~) vblank::$9) ← (byte) 1 -- *(vblank::$15 + OFFSET_STRUCT_RICOH_2A03_JOY1) +Converting *(pointer+n) to pointer[n] [58] *((byte*~) vblank::$10) ← (byte) 0 -- *(vblank::$16 + OFFSET_STRUCT_RICOH_2A03_JOY1) +Converting *(pointer+n) to pointer[n] [61] (byte~) vblank::$1 ← *((byte*~) vblank::$11) & (byte) 1 -- *(vblank::$17 + OFFSET_STRUCT_RICOH_2A03_JOY1) +Converting *(pointer+n) to pointer[n] [66] (byte~) vblank::$3 ← *((byte*~) vblank::$12) & (byte) 1 -- *(vblank::$18 + OFFSET_STRUCT_RICOH_2A03_JOY1) +Converting *(pointer+n) to pointer[n] [85] *((byte*~) initPaletteData::$2) ← >(const nomodify byte*) PPU_PALETTE -- *(initPaletteData::$5 + OFFSET_STRUCT_RICOH_2C02_PPUADDR) +Converting *(pointer+n) to pointer[n] [88] *((byte*~) initPaletteData::$3) ← <(const nomodify byte*) PPU_PALETTE -- *(initPaletteData::$6 + OFFSET_STRUCT_RICOH_2C02_PPUADDR) +Converting *(pointer+n) to pointer[n] [96] *((byte*~) initPaletteData::$4) ← *((const byte*) PALETTE + (byte) initPaletteData::i#2) -- *(initPaletteData::$7 + OFFSET_STRUCT_RICOH_2C02_PPUDATA) +Successful SSA optimization Pass2InlineDerefIdx +Simplifying constant evaluating to zero <(const nomodify byte*) PPU_PALETTE in [88] *((const byte*) initPaletteData::$6 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR) ← <(const nomodify byte*) PPU_PALETTE +Successful SSA optimization PassNSimplifyConstantZero +Simplifying expression containing zero main::disableVideoOutput1_$2 in [2] (byte*~) main::disableVideoOutput1_$0 ← (const byte*) main::disableVideoOutput1_$2 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUCTRL +Simplifying expression containing zero main::disableVideoOutput1_$2 in [3] *((const byte*) main::disableVideoOutput1_$2 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUCTRL) ← (byte) 0 +Simplifying expression containing zero MEMORY in [19] *((const nomodify byte*) MEMORY+(byte) 0 + (byte) main::i#2) ← (byte) 0 +Simplifying expression containing zero main::enableVideoOutput1_$2 in [38] (byte*~) main::enableVideoOutput1_$0 ← (const byte*) main::enableVideoOutput1_$2 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUCTRL +Simplifying expression containing zero main::enableVideoOutput1_$2 in [39] *((const byte*) main::enableVideoOutput1_$2 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUCTRL) ← (byte) $80 +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused variable (byte*~) main::disableVideoOutput1_$0 and assignment [1] (byte*~) main::disableVideoOutput1_$0 ← (const byte*) main::disableVideoOutput1_$2 +Eliminating unused variable (byte*~) main::disableVideoOutput1_$1 and assignment [3] (byte*~) main::disableVideoOutput1_$1 ← (const byte*) main::disableVideoOutput1_$3 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK +Eliminating unused variable (byte*~) main::disableAudioOutput1_$0 and assignment [6] (byte*~) main::disableAudioOutput1_$0 ← (const byte*) main::disableAudioOutput1_$1 + (const byte) OFFSET_STRUCT_RICOH_2A03_DMC_FREQ +Eliminating unused variable (byte*~) main::waitForVBlank1_$2 and assignment [9] (byte*~) main::waitForVBlank1_$2 ← (const byte*) main::waitForVBlank1_$3 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS +Eliminating unused variable (byte*~) main::waitForVBlank2_$2 and assignment [23] (byte*~) main::waitForVBlank2_$2 ← (const byte*) main::waitForVBlank2_$3 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS +Eliminating unused variable (byte*~) main::enableVideoOutput1_$0 and assignment [28] (byte*~) main::enableVideoOutput1_$0 ← (const byte*) main::enableVideoOutput1_$2 +Eliminating unused variable (byte*~) main::enableVideoOutput1_$1 and assignment [30] (byte*~) main::enableVideoOutput1_$1 ← (const byte*) main::enableVideoOutput1_$3 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK +Eliminating unused variable (byte*~) vblank::$7 and assignment [33] (byte*~) vblank::$7 ← (const byte*) vblank::$13 + (const byte) OFFSET_STRUCT_RICOH_2C02_OAMADDR +Eliminating unused variable (byte*~) vblank::$8 and assignment [35] (byte*~) vblank::$8 ← (const byte*) vblank::$14 + (const byte) OFFSET_STRUCT_RICOH_2A03_OAMDMA +Eliminating unused variable (byte*~) vblank::$9 and assignment [37] (byte*~) vblank::$9 ← (const byte*) vblank::$15 + (const byte) OFFSET_STRUCT_RICOH_2A03_JOY1 +Eliminating unused variable (byte*~) vblank::$10 and assignment [39] (byte*~) vblank::$10 ← (const byte*) vblank::$16 + (const byte) OFFSET_STRUCT_RICOH_2A03_JOY1 +Eliminating unused variable (byte*~) vblank::$11 and assignment [41] (byte*~) vblank::$11 ← (const byte*) vblank::$17 + (const byte) OFFSET_STRUCT_RICOH_2A03_JOY1 +Eliminating unused variable (byte*~) vblank::$12 and assignment [44] (byte*~) vblank::$12 ← (const byte*) vblank::$18 + (const byte) OFFSET_STRUCT_RICOH_2A03_JOY1 +Eliminating unused variable (byte*~) initPaletteData::$2 and assignment [61] (byte*~) initPaletteData::$2 ← (const byte*) initPaletteData::$5 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR +Eliminating unused variable (byte*~) initPaletteData::$3 and assignment [63] (byte*~) initPaletteData::$3 ← (const byte*) initPaletteData::$6 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR +Eliminating unused variable (byte*~) initPaletteData::$4 and assignment [67] (byte*~) initPaletteData::$4 ← (const byte*) initPaletteData::$7 + (const byte) OFFSET_STRUCT_RICOH_2C02_PPUDATA +Eliminating unused constant (const byte) OFFSET_STRUCT_RICOH_2C02_PPUCTRL +Successful SSA optimization PassNEliminateUnusedVars +Removing unused block main::@return +Successful SSA optimization Pass2EliminateUnusedBlocks +Resolving array sizeof() sizeof (const byte*) PALETTE +Resolving array sizeof() sizeof (const byte*) SPRITES +Successful SSA optimization PassNSizeOfSimplification +Adding number conversion cast (unumber) $20 in +Adding number conversion cast (unumber) $10 in +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant integer cast $20 +Simplifying constant integer cast $10 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) $20 +Finalized unsigned number type (byte) $10 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Inlining constant with var siblings (const byte) main::i#0 +Inlining constant with var siblings (const byte) initPaletteData::i#0 +Inlining constant with var siblings (const byte) initSpriteData::i#0 +Constant inlined vblank::$13 = (byte*)(const struct RICOH_2C02*) PPU +Constant inlined initPaletteData::$5 = (byte*)(const struct RICOH_2C02*) PPU +Constant inlined initSpriteData::i#0 = (byte) 0 +Constant inlined vblank::$14 = (byte*)(const struct RICOH_2A03*) APU +Constant inlined initPaletteData::$6 = (byte*)(const struct RICOH_2C02*) PPU +Constant inlined vblank::$15 = (byte*)(const struct RICOH_2A03*) APU +Constant inlined vblank::$16 = (byte*)(const struct RICOH_2A03*) APU +Constant inlined main::disableVideoOutput1_$2 = (byte*)(const struct RICOH_2C02*) PPU +Constant inlined vblank::$17 = (byte*)(const struct RICOH_2A03*) APU +Constant inlined initSpriteData::$0 = (byte) $10*(const byte) SIZEOF_BYTE +Constant inlined vblank::$18 = (byte*)(const struct RICOH_2A03*) APU +Constant inlined main::disableVideoOutput1_$3 = (byte*)(const struct RICOH_2C02*) PPU +Constant inlined initPaletteData::$0 = (byte) $20*(const byte) SIZEOF_BYTE +Constant inlined main::enableVideoOutput1_$3 = (byte*)(const struct RICOH_2C02*) PPU +Constant inlined main::enableVideoOutput1_$2 = (byte*)(const struct RICOH_2C02*) PPU +Constant inlined initPaletteData::$7 = (byte*)(const struct RICOH_2C02*) PPU +Constant inlined main::waitForVBlank2_$3 = (byte*)(const struct RICOH_2C02*) PPU +Constant inlined main::waitForVBlank1_$3 = (byte*)(const struct RICOH_2C02*) PPU +Constant inlined vblank::$0 = >(const nomodify byte*) OAM_BUFFER +Constant inlined main::i#0 = (byte) 0 +Constant inlined main::disableAudioOutput1_$1 = (byte*)(const struct RICOH_2A03*) APU +Constant inlined initPaletteData::i#0 = (byte) 0 +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *((byte*)PPU+OFFSET_STRUCT_RICOH_2C02_PPUMASK) +Consolidated array index constant in *((byte*)APU+OFFSET_STRUCT_RICOH_2A03_DMC_FREQ) +Consolidated array index constant in *((byte*)PPU+OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) +Consolidated array index constant in *((byte*)PPU+OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) +Consolidated array index constant in *((byte*)PPU+OFFSET_STRUCT_RICOH_2C02_PPUMASK) +Consolidated array index constant in *((byte*)PPU+OFFSET_STRUCT_RICOH_2C02_OAMADDR) +Consolidated array index constant in *((byte*)APU+OFFSET_STRUCT_RICOH_2A03_OAMDMA) +Consolidated array index constant in *((byte*)APU+OFFSET_STRUCT_RICOH_2A03_JOY1) +Consolidated array index constant in *((byte*)APU+OFFSET_STRUCT_RICOH_2A03_JOY1) +Consolidated array index constant in *((byte*)APU+OFFSET_STRUCT_RICOH_2A03_JOY1) +Consolidated array index constant in *((byte*)APU+OFFSET_STRUCT_RICOH_2A03_JOY1) +Consolidated array index constant in *(OAM_BUFFER+3) +Consolidated array index constant in *(OAM_BUFFER+3) +Consolidated array index constant in *(OAM_BUFFER+7) +Consolidated array index constant in *(OAM_BUFFER+7) +Consolidated array index constant in *(OAM_BUFFER+$b) +Consolidated array index constant in *(OAM_BUFFER+$b) +Consolidated array index constant in *(OAM_BUFFER+$f) +Consolidated array index constant in *(OAM_BUFFER+$f) +Consolidated array index constant in *(OAM_BUFFER+3) +Consolidated array index constant in *(OAM_BUFFER+3) +Consolidated array index constant in *(OAM_BUFFER+7) +Consolidated array index constant in *(OAM_BUFFER+7) +Consolidated array index constant in *(OAM_BUFFER+$b) +Consolidated array index constant in *(OAM_BUFFER+$b) +Consolidated array index constant in *(OAM_BUFFER+$f) +Consolidated array index constant in *(OAM_BUFFER+$f) +Consolidated array index constant in *((byte*)PPU+OFFSET_STRUCT_RICOH_2C02_PPUADDR) +Consolidated array index constant in *((byte*)PPU+OFFSET_STRUCT_RICOH_2C02_PPUADDR) +Consolidated array index constant in *((byte*)PPU+OFFSET_STRUCT_RICOH_2C02_PPUDATA) +Successful SSA optimization Pass2ConstantAdditionElimination +Added new block during phi lifting main::@7(between main::@1 and main::@1) +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main::waitForVBlank1 +Adding NOP phi() at start of main::@3 +Adding NOP phi() at start of main::waitForVBlank2 +Adding NOP phi() at start of main::@4 +Adding NOP phi() at start of main::@5 +Adding NOP phi() at start of main::@6 +Adding NOP phi() at start of main::@2 +Adding NOP phi() at start of initSpriteData +Adding NOP phi() at start of vblank::@2 +Adding NOP phi() at start of vblank::@4 +Adding NOP phi() at start of vblank::@3 +Adding NOP phi() at start of vblank::@5 +CALL GRAPH +Calls in [] to main:2 +Calls in [main] to initPaletteData:30 initSpriteData:32 +Calls in [vblank] to moveLuigiRight:61 moveLuigiLeft:66 + +Created 3 initial phi equivalence classes +Coalesced [37] main::i#3 ← main::i#1 +Coalesced [44] initSpriteData::i#4 ← initSpriteData::i#1 +Coalesced [53] initPaletteData::i#4 ← initPaletteData::i#1 +Coalesced down to 3 phi equivalence classes +Culled Empty Block (label) @2 +Culled Empty Block (label) main::@3 +Culled Empty Block (label) main::@6 +Culled Empty Block (label) main::@7 +Culled Empty Block (label) vblank::@4 +Culled Empty Block (label) vblank::@5 +Renumbering block main::@4 to main::@3 +Renumbering block main::@5 to main::@4 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main::waitForVBlank1 +Adding NOP phi() at start of main::waitForVBlank2 +Adding NOP phi() at start of main::@3 +Adding NOP phi() at start of main::@4 +Adding NOP phi() at start of main::@2 +Adding NOP phi() at start of initSpriteData +Adding NOP phi() at start of vblank::@2 +Adding NOP phi() at start of vblank::@3 + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() + +(void()) main() +main: scope:[main] from @1 + asm { cld ldx#$ff txs } + to:main::disableVideoOutput1 +main::disableVideoOutput1: scope:[main] from main + [5] *((byte*)(const struct RICOH_2C02*) PPU) ← (byte) 0 + [6] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK) ← (byte) 0 + to:main::disableAudioOutput1 +main::disableAudioOutput1: scope:[main] from main::disableVideoOutput1 + [7] *((const nomodify byte*) FR_COUNTER) ← (byte) $40 + [8] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_DMC_FREQ) ← (byte) $40 + to:main::clearVBlankFlag1 +main::clearVBlankFlag1: scope:[main] from main::disableAudioOutput1 + asm { ldaPPU_PPUSTATUS } + to:main::waitForVBlank1 +main::waitForVBlank1: scope:[main] from main::clearVBlankFlag1 + [10] phi() + to:main::waitForVBlank1_@1 +main::waitForVBlank1_@1: scope:[main] from main::waitForVBlank1 main::waitForVBlank1_@1 + [11] (byte~) main::waitForVBlank1_$0 ← *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) & (byte) $80 + [12] if((byte) 0==(byte~) main::waitForVBlank1_$0) goto main::waitForVBlank1_@1 + to:main::@1 +main::@1: scope:[main] from main::@1 main::waitForVBlank1_@1 + [13] (byte) main::i#2 ← phi( main::@1/(byte) main::i#1 main::waitForVBlank1_@1/(byte) 0 ) + [14] *((const nomodify byte*) MEMORY + (byte) main::i#2) ← (byte) 0 + [15] *((const nomodify byte*) MEMORY+(word) $100 + (byte) main::i#2) ← (byte) 0 + [16] *((const nomodify byte*) MEMORY+(word) $200 + (byte) main::i#2) ← (byte) 0 + [17] *((const nomodify byte*) MEMORY+(word) $300 + (byte) main::i#2) ← (byte) 0 + [18] *((const nomodify byte*) MEMORY+(word) $400 + (byte) main::i#2) ← (byte) 0 + [19] *((const nomodify byte*) MEMORY+(word) $500 + (byte) main::i#2) ← (byte) 0 + [20] *((const nomodify byte*) MEMORY+(word) $600 + (byte) main::i#2) ← (byte) 0 + [21] *((const nomodify byte*) MEMORY+(word) $700 + (byte) main::i#2) ← (byte) 0 + [22] (byte) main::i#1 ← ++ (byte) main::i#2 + [23] if((byte) 0!=(byte) main::i#1) goto main::@1 + to:main::waitForVBlank2 +main::waitForVBlank2: scope:[main] from main::@1 + [24] phi() + to:main::waitForVBlank2_@1 +main::waitForVBlank2_@1: scope:[main] from main::waitForVBlank2 main::waitForVBlank2_@1 + [25] (byte~) main::waitForVBlank2_$0 ← *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) & (byte) $80 + [26] if((byte) 0==(byte~) main::waitForVBlank2_$0) goto main::waitForVBlank2_@1 + to:main::@3 +main::@3: scope:[main] from main::waitForVBlank2_@1 + [27] phi() + [28] call initPaletteData + to:main::@4 +main::@4: scope:[main] from main::@3 + [29] phi() + [30] call initSpriteData + to:main::enableVideoOutput1 +main::enableVideoOutput1: scope:[main] from main::@4 + [31] *((byte*)(const struct RICOH_2C02*) PPU) ← (byte) $80 + [32] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK) ← (byte) $10 + to:main::@2 +main::@2: scope:[main] from main::@2 main::enableVideoOutput1 + [33] phi() + to:main::@2 + +(void()) initSpriteData() +initSpriteData: scope:[initSpriteData] from main::@4 + [34] phi() + to:initSpriteData::@1 +initSpriteData::@1: scope:[initSpriteData] from initSpriteData initSpriteData::@2 + [35] (byte) initSpriteData::i#2 ← phi( initSpriteData/(byte) 0 initSpriteData::@2/(byte) initSpriteData::i#1 ) + [36] if((byte) initSpriteData::i#2<(byte) $10*(const byte) SIZEOF_BYTE) goto initSpriteData::@2 + to:initSpriteData::@return +initSpriteData::@return: scope:[initSpriteData] from initSpriteData::@1 + [37] return + to:@return +initSpriteData::@2: scope:[initSpriteData] from initSpriteData::@1 + [38] *((const nomodify byte*) OAM_BUFFER + (byte) initSpriteData::i#2) ← *((const byte*) SPRITES + (byte) initSpriteData::i#2) + [39] (byte) initSpriteData::i#1 ← ++ (byte) initSpriteData::i#2 + to:initSpriteData::@1 + +(void()) initPaletteData() +initPaletteData: scope:[initPaletteData] from main::@3 + asm { ldaPPU_PPUSTATUS } + [41] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR) ← >(const nomodify byte*) PPU_PALETTE + [42] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR) ← (byte) 0 + to:initPaletteData::@1 +initPaletteData::@1: scope:[initPaletteData] from initPaletteData initPaletteData::@2 + [43] (byte) initPaletteData::i#2 ← phi( initPaletteData/(byte) 0 initPaletteData::@2/(byte) initPaletteData::i#1 ) + [44] if((byte) initPaletteData::i#2<(byte) $20*(const byte) SIZEOF_BYTE) goto initPaletteData::@2 + to:initPaletteData::@return +initPaletteData::@return: scope:[initPaletteData] from initPaletteData::@1 + [45] return + to:@return +initPaletteData::@2: scope:[initPaletteData] from initPaletteData::@1 + [46] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUDATA) ← *((const byte*) PALETTE + (byte) initPaletteData::i#2) + [47] (byte) initPaletteData::i#1 ← ++ (byte) initPaletteData::i#2 + to:initPaletteData::@1 + +interrupt(HARDWARE_STACK)(void()) vblank() +vblank: scope:[vblank] from + [48] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_OAMADDR) ← (byte) 0 + [49] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_OAMDMA) ← >(const nomodify byte*) OAM_BUFFER + [50] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) ← (byte) 1 + [51] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) ← (byte) 0 + [52] (byte~) vblank::$1 ← *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) & (byte) 1 + [53] if((byte) 0==(byte~) vblank::$1) goto vblank::@1 + to:vblank::@2 +vblank::@2: scope:[vblank] from vblank + [54] phi() + [55] call moveLuigiRight + to:vblank::@1 +vblank::@1: scope:[vblank] from vblank vblank::@2 + [56] (byte~) vblank::$3 ← *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) & (byte) 1 + [57] if((byte) 0==(byte~) vblank::$3) goto vblank::@return + to:vblank::@3 +vblank::@3: scope:[vblank] from vblank::@1 + [58] phi() + [59] call moveLuigiLeft + to:vblank::@return +vblank::@return: scope:[vblank] from vblank::@1 vblank::@3 + [60] return + to:@return + +(void()) moveLuigiLeft() +moveLuigiLeft: scope:[moveLuigiLeft] from vblank::@3 + [61] *((const nomodify byte*) OAM_BUFFER+(byte) 3) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) 3) + [62] *((const nomodify byte*) OAM_BUFFER+(byte) 7) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) 7) + [63] *((const nomodify byte*) OAM_BUFFER+(byte) $b) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) $b) + [64] *((const nomodify byte*) OAM_BUFFER+(byte) $f) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) $f) + to:moveLuigiLeft::@return +moveLuigiLeft::@return: scope:[moveLuigiLeft] from moveLuigiLeft + [65] return + to:@return + +(void()) moveLuigiRight() +moveLuigiRight: scope:[moveLuigiRight] from vblank::@2 + [66] *((const nomodify byte*) OAM_BUFFER+(byte) 3) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) 3) + [67] *((const nomodify byte*) OAM_BUFFER+(byte) 7) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) 7) + [68] *((const nomodify byte*) OAM_BUFFER+(byte) $b) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) $b) + [69] *((const nomodify byte*) OAM_BUFFER+(byte) $f) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) $f) + to:moveLuigiRight::@return +moveLuigiRight::@return: scope:[moveLuigiRight] from moveLuigiRight + [70] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte) RICOH_2A03::DMC_FREQ +(byte) RICOH_2A03::DMC_LEN +(byte) RICOH_2A03::DMC_RAW +(byte) RICOH_2A03::DMC_START +(byte) RICOH_2A03::JOY1 +(byte) RICOH_2A03::JOY2 +(byte) RICOH_2A03::NOISE_HI +(byte) RICOH_2A03::NOISE_LO +(byte) RICOH_2A03::NOISE_VOL +(byte) RICOH_2A03::OAMDMA +(byte) RICOH_2A03::SND_CHN +(byte) RICOH_2A03::SQ1_HI +(byte) RICOH_2A03::SQ1_LO +(byte) RICOH_2A03::SQ1_SWEEP +(byte) RICOH_2A03::SQ1_VOL +(byte) RICOH_2A03::SQ2_HI +(byte) RICOH_2A03::SQ2_LO +(byte) RICOH_2A03::SQ2_SWEEP +(byte) RICOH_2A03::SQ2_VOL +(byte) RICOH_2A03::TRI_HI +(byte) RICOH_2A03::TRI_LINEAR +(byte) RICOH_2A03::TRI_LO +(byte) RICOH_2A03::UNUSED1 +(byte) RICOH_2A03::UNUSED2 +(byte) RICOH_2C02::OAMADDR +(byte) RICOH_2C02::OAMDATA +(byte) RICOH_2C02::PPUADDR +(byte) RICOH_2C02::PPUCTRL +(byte) RICOH_2C02::PPUDATA +(byte) RICOH_2C02::PPUMASK +(byte) RICOH_2C02::PPUSCROLL +(volatile byte) RICOH_2C02::PPUSTATUS loadstore +(void()) initPaletteData() +(byte) initPaletteData::i +(byte) initPaletteData::i#1 2002.0 +(byte) initPaletteData::i#2 1334.6666666666667 +(void()) initSpriteData() +(byte) initSpriteData::i +(byte) initSpriteData::i#1 2002.0 +(byte) initSpriteData::i#2 1668.3333333333335 +(void()) main() +(byte) main::i +(byte) main::i#1 151.5 +(byte) main::i#2 112.22222222222223 +(byte~) main::waitForVBlank1_$0 202.0 +(byte~) main::waitForVBlank2_$0 202.0 +(void()) moveLuigiLeft() +(void()) moveLuigiRight() +interrupt(HARDWARE_STACK)(void()) vblank() +(byte~) vblank::$1 4.0 +(byte~) vblank::$3 4.0 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +[ initSpriteData::i#2 initSpriteData::i#1 ] +[ initPaletteData::i#2 initPaletteData::i#1 ] +Added variable main::waitForVBlank1_$0 to live range equivalence class [ main::waitForVBlank1_$0 ] +Added variable main::waitForVBlank2_$0 to live range equivalence class [ main::waitForVBlank2_$0 ] +Added variable vblank::$1 to live range equivalence class [ vblank::$1 ] +Added variable vblank::$3 to live range equivalence class [ vblank::$3 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ initSpriteData::i#2 initSpriteData::i#1 ] +[ initPaletteData::i#2 initPaletteData::i#1 ] +[ main::waitForVBlank1_$0 ] +[ main::waitForVBlank2_$0 ] +[ vblank::$1 ] +[ vblank::$3 ] +Allocated zp[1]:2 [ main::i#2 main::i#1 ] +Allocated zp[1]:3 [ initSpriteData::i#2 initSpriteData::i#1 ] +Allocated zp[1]:4 [ initPaletteData::i#2 initPaletteData::i#1 ] +Allocated zp[1]:5 [ main::waitForVBlank1_$0 ] +Allocated zp[1]:6 [ main::waitForVBlank2_$0 ] +Allocated zp[1]:7 [ vblank::$1 ] +Allocated zp[1]:8 [ vblank::$3 ] + +INITIAL ASM +Target platform is nes / MOS6502 + // File Comments +// A minimal NES demo +// Based on: https://github.com/gregkrsak/first_nes written by Greg M. Krsak, 2018. + // Upstart + // Nintendo Entertainment System (NES) ROM +// https://sadistech.com/nesromtool/romdoc.html +// https://forums.nesdev.com/viewtopic.php?f=2&t=9896 +// https://github.com/gregkrsak/first_nes +.file [name="nes-demo.nes", type="bin", segments="NesRom"] +.file [name="nes-demo.nes_hdr", type="bin", segments="Header"] +.file [name="nes-demo.nes_prg", type="bin", segments="ProgramRom"] +.file [name="nes-demo.nes_chr", type="bin", segments="CharacterRom"] +.segmentdef Header [ start=$0000, min=$0000, max=$000f, fill ] +.segmentdef Tiles [ start=$0000, min=$0000, max=$1fff, fill ] +.segmentdef Code [ start=$c000, min=$c000, max=$fff9 ] +.segmentdef Data [ startAfter="Code", min=$c000, max=$fff9 ] +.segmentdef Vectors [ start=$fffa, min=$fffa, max=$ffff ] +.segmentdef ProgramRom [ segments="Code, Data, Vectors" ] +.segmentdef CharacterRom [ segments="Tiles" ] +.segmentdef NesRom +//.segment NesRom +//.segmentout [ segments="Header" ] +//.segmentout [ segments="ProgramRom" ] +//.segmentout [ segments="CharacterRom" ] +.segment Header +.text @"NES\$1a" +.byte $01 // 1x 16KB ROM (PRG) +.byte $01 // 1x 8KB VROM (CHR) +.byte %00000001 // Mapper nibble 0000 == No mapping (a simple 16KB PRG + 8KB CHR game) + // Mirroring nibble 0001 == Vertical mirroring only +.segment Code + + // Global Constants & labels + .const OFFSET_STRUCT_RICOH_2A03_DMC_FREQ = $10 + .const OFFSET_STRUCT_RICOH_2C02_PPUMASK = 1 + .const OFFSET_STRUCT_RICOH_2C02_PPUSTATUS = 2 + .const OFFSET_STRUCT_RICOH_2C02_OAMADDR = 3 + .const OFFSET_STRUCT_RICOH_2A03_OAMDMA = $14 + .const OFFSET_STRUCT_RICOH_2A03_JOY1 = $16 + .const OFFSET_STRUCT_RICOH_2C02_PPUADDR = 6 + .const OFFSET_STRUCT_RICOH_2C02_PPUDATA = 7 + .const SIZEOF_BYTE = 1 + // $3000-$3EFF $0F00 Mirrors of $2000-$2EFF + // $3F00-$3F1F $0020 Palette RAM indexes + .label PPU_PALETTE = $3f00 + // APU Frame Counter + // generates low-frequency clocks for the channels and an optional 60 Hz interrupt. + // https://wiki.nesdev.com/w/index.php/APU_Frame_Counter + // ------+-----+--------------------------------------------------------------- + // $4017 | W | FR_COUNTER Frame Counter Set mode and interrupt + // ------+-----+--------------------------------------------------------------- + // | 7 | Sequencer mode: 0 selects 4-step sequence, 1 selects 5-step sequence + // | 6 | Interrupt inhibit flag. If set, the frame interrupt flag is cleared, otherwise it is unaffected. + // ------+-----+--------------------------------------------------------------- + // Side effects After 3 or 4 CPU clock cycles*, the timer is reset. + // If the mode flag is set, then both "quarter frame" and "half frame" signals are also generated. + .label FR_COUNTER = $4017 + // Pointer to the start of RAM memory + .label MEMORY = 0 + // OAM (Object Attribute Memory) Buffer + // Will be transfered to the PPU via DMA + .label OAM_BUFFER = $200 + // PPU Status Register for reading in ASM + .label PPU_PPUSTATUS = $2002 + // NES Picture Processing Unit (PPU) + .label PPU = $2000 + // NES CPU and audion processing unit (APU) + .label APU = $4000 + // @begin +__bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +__b1_from___bbegin: + jmp __b1 + // @1 +__b1: + // [2] call main + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: +.segment Code + // main +// RESET Called when the NES is reset, including when it is turned on. +main: { + .label waitForVBlank1___0 = 5 + .label waitForVBlank2___0 = 6 + // Clear RAM - since it has all variables and the stack it is necesary to do it inline + .label i = 2 + // asm { cld ldx#$ff txs } + cld + ldx #$ff + txs + jmp disableVideoOutput1 + // main::disableVideoOutput1 + disableVideoOutput1: + // [5] *((byte*)(const struct RICOH_2C02*) PPU) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta PPU + // [6] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUMASK + jmp disableAudioOutput1 + // main::disableAudioOutput1 + disableAudioOutput1: + // [7] *((const nomodify byte*) FR_COUNTER) ← (byte) $40 -- _deref_pbuc1=vbuc2 + lda #$40 + sta FR_COUNTER + // [8] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_DMC_FREQ) ← (byte) $40 -- _deref_pbuc1=vbuc2 + lda #$40 + sta APU+OFFSET_STRUCT_RICOH_2A03_DMC_FREQ + jmp clearVBlankFlag1 + // main::clearVBlankFlag1 + clearVBlankFlag1: + // asm { ldaPPU_PPUSTATUS } + lda PPU_PPUSTATUS + // [10] phi from main::clearVBlankFlag1 to main::waitForVBlank1 [phi:main::clearVBlankFlag1->main::waitForVBlank1] + waitForVBlank1_from_clearVBlankFlag1: + jmp waitForVBlank1 + // main::waitForVBlank1 + waitForVBlank1: + jmp waitForVBlank1___b1 + // main::waitForVBlank1_@1 + waitForVBlank1___b1: + // [11] (byte~) main::waitForVBlank1_$0 ← *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) & (byte) $80 -- vbuz1=_deref_pbuc1_band_vbuc2 + lda #$80 + and PPU+OFFSET_STRUCT_RICOH_2C02_PPUSTATUS + sta.z waitForVBlank1___0 + // [12] if((byte) 0==(byte~) main::waitForVBlank1_$0) goto main::waitForVBlank1_@1 -- vbuc1_eq_vbuz1_then_la1 + lda #0 + cmp.z waitForVBlank1___0 + beq waitForVBlank1___b1 + // [13] phi from main::waitForVBlank1_@1 to main::@1 [phi:main::waitForVBlank1_@1->main::@1] + __b1_from_waitForVBlank1___b1: + // [13] phi (byte) main::i#2 = (byte) 0 [phi:main::waitForVBlank1_@1->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp __b1 + // [13] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + __b1_from___b1: + // [13] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#0] -- register_copy + jmp __b1 + // main::@1 + __b1: + // [14] *((const nomodify byte*) MEMORY + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuz1=vbuc2 + lda #0 + ldy.z i + sta MEMORY,y + // [15] *((const nomodify byte*) MEMORY+(word) $100 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuz1=vbuc2 + lda #0 + ldy.z i + sta MEMORY+$100,y + // [16] *((const nomodify byte*) MEMORY+(word) $200 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuz1=vbuc2 + lda #0 + ldy.z i + sta MEMORY+$200,y + // [17] *((const nomodify byte*) MEMORY+(word) $300 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuz1=vbuc2 + lda #0 + ldy.z i + sta MEMORY+$300,y + // [18] *((const nomodify byte*) MEMORY+(word) $400 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuz1=vbuc2 + lda #0 + ldy.z i + sta MEMORY+$400,y + // [19] *((const nomodify byte*) MEMORY+(word) $500 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuz1=vbuc2 + lda #0 + ldy.z i + sta MEMORY+$500,y + // [20] *((const nomodify byte*) MEMORY+(word) $600 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuz1=vbuc2 + lda #0 + ldy.z i + sta MEMORY+$600,y + // [21] *((const nomodify byte*) MEMORY+(word) $700 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuz1=vbuc2 + lda #0 + ldy.z i + sta MEMORY+$700,y + // [22] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc.z i + // [23] if((byte) 0!=(byte) main::i#1) goto main::@1 -- vbuc1_neq_vbuz1_then_la1 + lda #0 + cmp.z i + bne __b1_from___b1 + // [24] phi from main::@1 to main::waitForVBlank2 [phi:main::@1->main::waitForVBlank2] + waitForVBlank2_from___b1: + jmp waitForVBlank2 + // main::waitForVBlank2 + waitForVBlank2: + jmp waitForVBlank2___b1 + // main::waitForVBlank2_@1 + waitForVBlank2___b1: + // [25] (byte~) main::waitForVBlank2_$0 ← *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) & (byte) $80 -- vbuz1=_deref_pbuc1_band_vbuc2 + lda #$80 + and PPU+OFFSET_STRUCT_RICOH_2C02_PPUSTATUS + sta.z waitForVBlank2___0 + // [26] if((byte) 0==(byte~) main::waitForVBlank2_$0) goto main::waitForVBlank2_@1 -- vbuc1_eq_vbuz1_then_la1 + lda #0 + cmp.z waitForVBlank2___0 + beq waitForVBlank2___b1 + // [27] phi from main::waitForVBlank2_@1 to main::@3 [phi:main::waitForVBlank2_@1->main::@3] + __b3_from_waitForVBlank2___b1: + jmp __b3 + // main::@3 + __b3: + // [28] call initPaletteData + // Now the PPU is ready. + jsr initPaletteData + // [29] phi from main::@3 to main::@4 [phi:main::@3->main::@4] + __b4_from___b3: + jmp __b4 + // main::@4 + __b4: + // [30] call initSpriteData + // [34] phi from main::@4 to initSpriteData [phi:main::@4->initSpriteData] + initSpriteData_from___b4: + jsr initSpriteData + jmp enableVideoOutput1 + // main::enableVideoOutput1 + enableVideoOutput1: + // [31] *((byte*)(const struct RICOH_2C02*) PPU) ← (byte) $80 -- _deref_pbuc1=vbuc2 + lda #$80 + sta PPU + // [32] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK) ← (byte) $10 -- _deref_pbuc1=vbuc2 + lda #$10 + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUMASK + // [33] phi from main::@2 main::enableVideoOutput1 to main::@2 [phi:main::@2/main::enableVideoOutput1->main::@2] + __b2_from___b2: + __b2_from_enableVideoOutput1: + jmp __b2 + // Infinite loop + // main::@2 + __b2: + jmp __b2_from___b2 +} + // initSpriteData +// Initialize OAM (Object Attribute Memory) Buffer +initSpriteData: { + .label i = 3 + // [35] phi from initSpriteData to initSpriteData::@1 [phi:initSpriteData->initSpriteData::@1] + __b1_from_initSpriteData: + // [35] phi (byte) initSpriteData::i#2 = (byte) 0 [phi:initSpriteData->initSpriteData::@1#0] -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp __b1 + // initSpriteData::@1 + __b1: + // [36] if((byte) initSpriteData::i#2<(byte) $10*(const byte) SIZEOF_BYTE) goto initSpriteData::@2 -- vbuz1_lt_vbuc1_then_la1 + lda.z i + cmp #$10*SIZEOF_BYTE + bcc __b2 + jmp __breturn + // initSpriteData::@return + __breturn: + // [37] return + rts + // initSpriteData::@2 + __b2: + // [38] *((const nomodify byte*) OAM_BUFFER + (byte) initSpriteData::i#2) ← *((const byte*) SPRITES + (byte) initSpriteData::i#2) -- pbuc1_derefidx_vbuz1=pbuc2_derefidx_vbuz1 + ldy.z i + lda SPRITES,y + sta OAM_BUFFER,y + // [39] (byte) initSpriteData::i#1 ← ++ (byte) initSpriteData::i#2 -- vbuz1=_inc_vbuz1 + inc.z i + // [35] phi from initSpriteData::@2 to initSpriteData::@1 [phi:initSpriteData::@2->initSpriteData::@1] + __b1_from___b2: + // [35] phi (byte) initSpriteData::i#2 = (byte) initSpriteData::i#1 [phi:initSpriteData::@2->initSpriteData::@1#0] -- register_copy + jmp __b1 +} + // initPaletteData +// Copy palette values to PPU +initPaletteData: { + .label i = 4 + // asm { ldaPPU_PPUSTATUS } + // Reset the high/low latch to "high" + lda PPU_PPUSTATUS + // [41] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR) ← >(const nomodify byte*) PPU_PALETTE -- _deref_pbuc1=vbuc2 + // Write the high byte of PPU Palette address + lda #>PPU_PALETTE + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUADDR + // [42] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // Write the low byte of PPU Palette address + lda #0 + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUADDR + // [43] phi from initPaletteData to initPaletteData::@1 [phi:initPaletteData->initPaletteData::@1] + __b1_from_initPaletteData: + // [43] phi (byte) initPaletteData::i#2 = (byte) 0 [phi:initPaletteData->initPaletteData::@1#0] -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp __b1 + // Write to PPU + // initPaletteData::@1 + __b1: + // [44] if((byte) initPaletteData::i#2<(byte) $20*(const byte) SIZEOF_BYTE) goto initPaletteData::@2 -- vbuz1_lt_vbuc1_then_la1 + lda.z i + cmp #$20*SIZEOF_BYTE + bcc __b2 + jmp __breturn + // initPaletteData::@return + __breturn: + // [45] return + rts + // initPaletteData::@2 + __b2: + // [46] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUDATA) ← *((const byte*) PALETTE + (byte) initPaletteData::i#2) -- _deref_pbuc1=pbuc2_derefidx_vbuz1 + ldy.z i + lda PALETTE,y + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUDATA + // [47] (byte) initPaletteData::i#1 ← ++ (byte) initPaletteData::i#2 -- vbuz1=_inc_vbuz1 + inc.z i + // [43] phi from initPaletteData::@2 to initPaletteData::@1 [phi:initPaletteData::@2->initPaletteData::@1] + __b1_from___b2: + // [43] phi (byte) initPaletteData::i#2 = (byte) initPaletteData::i#1 [phi:initPaletteData::@2->initPaletteData::@1#0] -- register_copy + jmp __b1 +} + // vblank +// NMI Called when the PPU refreshes the screen (also known as the V-Blank period) +vblank: { + .label __1 = 7 + .label __3 = 8 + // entry interrupt(HARDWARE_STACK) + pha + txa + pha + tya + pha + // [48] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_OAMADDR) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // Refresh DRAM-stored sprite data before it decays. + // Set OAM start address to sprite#0 + lda #0 + sta PPU+OFFSET_STRUCT_RICOH_2C02_OAMADDR + // [49] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_OAMDMA) ← >(const nomodify byte*) OAM_BUFFER -- _deref_pbuc1=vbuc2 + // Set the high byte (02) of the RAM address and start the DMA transfer to OAM memory + lda #>OAM_BUFFER + sta APU+OFFSET_STRUCT_RICOH_2A03_OAMDMA + // [50] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) ← (byte) 1 -- _deref_pbuc1=vbuc2 + // Freeze the button positions. + lda #1 + sta APU+OFFSET_STRUCT_RICOH_2A03_JOY1 + // [51] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta APU+OFFSET_STRUCT_RICOH_2A03_JOY1 + // [52] (byte~) vblank::$1 ← *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) & (byte) 1 -- vbuz1=_deref_pbuc1_band_vbuc2 + lda #1 + and APU+OFFSET_STRUCT_RICOH_2A03_JOY1 + sta.z __1 + // [53] if((byte) 0==(byte~) vblank::$1) goto vblank::@1 -- vbuc1_eq_vbuz1_then_la1 + lda #0 + cmp.z __1 + beq __b1 + // [54] phi from vblank to vblank::@2 [phi:vblank->vblank::@2] + __b2_from_vblank: + jmp __b2 + // vblank::@2 + __b2: + // [55] call moveLuigiRight + jsr moveLuigiRight + jmp __b1 + // vblank::@1 + __b1: + // [56] (byte~) vblank::$3 ← *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) & (byte) 1 -- vbuz1=_deref_pbuc1_band_vbuc2 + lda #1 + and APU+OFFSET_STRUCT_RICOH_2A03_JOY1 + sta.z __3 + // [57] if((byte) 0==(byte~) vblank::$3) goto vblank::@return -- vbuc1_eq_vbuz1_then_la1 + lda #0 + cmp.z __3 + beq __breturn + // [58] phi from vblank::@1 to vblank::@3 [phi:vblank::@1->vblank::@3] + __b3_from___b1: + jmp __b3 + // vblank::@3 + __b3: + // [59] call moveLuigiLeft + jsr moveLuigiLeft + jmp __breturn + // vblank::@return + __breturn: + // [60] return - exit interrupt(HARDWARE_STACK) + pla + tay + pla + tax + pla + rti +} + // moveLuigiLeft +// move the Luigi sprites left +moveLuigiLeft: { + // [61] *((const nomodify byte*) OAM_BUFFER+(byte) 3) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) 3) -- _deref_pbuc1=_dec__deref_pbuc1 + dec OAM_BUFFER+3 + // [62] *((const nomodify byte*) OAM_BUFFER+(byte) 7) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) 7) -- _deref_pbuc1=_dec__deref_pbuc1 + dec OAM_BUFFER+7 + // [63] *((const nomodify byte*) OAM_BUFFER+(byte) $b) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) $b) -- _deref_pbuc1=_dec__deref_pbuc1 + dec OAM_BUFFER+$b + // [64] *((const nomodify byte*) OAM_BUFFER+(byte) $f) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) $f) -- _deref_pbuc1=_dec__deref_pbuc1 + dec OAM_BUFFER+$f + jmp __breturn + // moveLuigiLeft::@return + __breturn: + // [65] return + rts +} + // moveLuigiRight +// move the Luigi sprites right +moveLuigiRight: { + // [66] *((const nomodify byte*) OAM_BUFFER+(byte) 3) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) 3) -- _deref_pbuc1=_inc__deref_pbuc1 + inc OAM_BUFFER+3 + // [67] *((const nomodify byte*) OAM_BUFFER+(byte) 7) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) 7) -- _deref_pbuc1=_inc__deref_pbuc1 + inc OAM_BUFFER+7 + // [68] *((const nomodify byte*) OAM_BUFFER+(byte) $b) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) $b) -- _deref_pbuc1=_inc__deref_pbuc1 + inc OAM_BUFFER+$b + // [69] *((const nomodify byte*) OAM_BUFFER+(byte) $f) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) $f) -- _deref_pbuc1=_inc__deref_pbuc1 + inc OAM_BUFFER+$f + jmp __breturn + // moveLuigiRight::@return + __breturn: + // [70] return + rts +} + // File Data +.segment Data + PALETTE: .byte $f, $31, $32, $33, $f, $35, $36, $37, $f, $39, $3a, $3b, $f, $3d, $3e, $f, $f, $1c, $15, $14, $f, 2, $38, $3c, $f, $30, $37, $1a, $f, $f, $f, $f + // Small Luigi Sprite Data + SPRITES: .byte $80, $36, 2, $80, $80, $37, 2, $88, $88, $38, 2, $80, $88, $39, 2, $88 +.segment Tiles +TILES: +.import binary "smb1_chr.bin" + +.segment Vectors + VECTORS: .word vblank, main, 0 +.segment NesRom +NES_ROM: +.segmentout [ segments="Header" ] +.segmentout [ segments="ProgramRom" ] +.segmentout [ segments="CharacterRom" ] + + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement asm { cld ldx#$ff txs } always clobbers reg byte x +Statement [5] *((byte*)(const struct RICOH_2C02*) PPU) ← (byte) 0 [ ] ( [ ] { } main:2 [ ] { } ) always clobbers reg byte a +Statement [6] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK) ← (byte) 0 [ ] ( [ ] { } main:2 [ ] { } ) always clobbers reg byte a +Statement [7] *((const nomodify byte*) FR_COUNTER) ← (byte) $40 [ ] ( [ ] { } main:2 [ ] { } ) always clobbers reg byte a +Statement [8] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_DMC_FREQ) ← (byte) $40 [ ] ( [ ] { } main:2 [ ] { } ) always clobbers reg byte a +Statement asm { ldaPPU_PPUSTATUS } always clobbers reg byte a +Statement [11] (byte~) main::waitForVBlank1_$0 ← *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) & (byte) $80 [ main::waitForVBlank1_$0 ] ( [ main::waitForVBlank1_$0 ] { } main:2 [ main::waitForVBlank1_$0 ] { } ) always clobbers reg byte a +Statement [14] *((const nomodify byte*) MEMORY + (byte) main::i#2) ← (byte) 0 [ main::i#2 ] ( [ main::i#2 ] { } main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:2 [ main::i#2 main::i#1 ] +Statement [15] *((const nomodify byte*) MEMORY+(word) $100 + (byte) main::i#2) ← (byte) 0 [ main::i#2 ] ( [ main::i#2 ] { } main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [16] *((const nomodify byte*) MEMORY+(word) $200 + (byte) main::i#2) ← (byte) 0 [ main::i#2 ] ( [ main::i#2 ] { } main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [17] *((const nomodify byte*) MEMORY+(word) $300 + (byte) main::i#2) ← (byte) 0 [ main::i#2 ] ( [ main::i#2 ] { } main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [18] *((const nomodify byte*) MEMORY+(word) $400 + (byte) main::i#2) ← (byte) 0 [ main::i#2 ] ( [ main::i#2 ] { } main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [19] *((const nomodify byte*) MEMORY+(word) $500 + (byte) main::i#2) ← (byte) 0 [ main::i#2 ] ( [ main::i#2 ] { } main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [20] *((const nomodify byte*) MEMORY+(word) $600 + (byte) main::i#2) ← (byte) 0 [ main::i#2 ] ( [ main::i#2 ] { } main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [21] *((const nomodify byte*) MEMORY+(word) $700 + (byte) main::i#2) ← (byte) 0 [ main::i#2 ] ( [ main::i#2 ] { } main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [25] (byte~) main::waitForVBlank2_$0 ← *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) & (byte) $80 [ main::waitForVBlank2_$0 ] ( [ main::waitForVBlank2_$0 ] { } main:2 [ main::waitForVBlank2_$0 ] { } ) always clobbers reg byte a +Statement [31] *((byte*)(const struct RICOH_2C02*) PPU) ← (byte) $80 [ ] ( [ ] { } main:2 [ ] { } ) always clobbers reg byte a +Statement [32] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK) ← (byte) $10 [ ] ( [ ] { } main:2 [ ] { } ) always clobbers reg byte a +Statement [38] *((const nomodify byte*) OAM_BUFFER + (byte) initSpriteData::i#2) ← *((const byte*) SPRITES + (byte) initSpriteData::i#2) [ initSpriteData::i#2 ] ( initSpriteData:30 [ initSpriteData::i#2 ] { } main:2::initSpriteData:30 [ initSpriteData::i#2 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:3 [ initSpriteData::i#2 initSpriteData::i#1 ] +Statement asm { ldaPPU_PPUSTATUS } always clobbers reg byte a +Statement [41] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR) ← >(const nomodify byte*) PPU_PALETTE [ ] ( initPaletteData:28 [ ] { } main:2::initPaletteData:28 [ ] { } ) always clobbers reg byte a +Statement [42] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR) ← (byte) 0 [ ] ( initPaletteData:28 [ ] { } main:2::initPaletteData:28 [ ] { } ) always clobbers reg byte a +Statement [46] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUDATA) ← *((const byte*) PALETTE + (byte) initPaletteData::i#2) [ initPaletteData::i#2 ] ( initPaletteData:28 [ initPaletteData::i#2 ] { } main:2::initPaletteData:28 [ initPaletteData::i#2 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:4 [ initPaletteData::i#2 initPaletteData::i#1 ] +Statement [48] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_OAMADDR) ← (byte) 0 [ ] ( [ ] { } ) always clobbers reg byte a +Statement [49] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_OAMDMA) ← >(const nomodify byte*) OAM_BUFFER [ ] ( [ ] { } ) always clobbers reg byte a +Statement [50] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) ← (byte) 1 [ ] ( [ ] { } ) always clobbers reg byte a +Statement [51] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) ← (byte) 0 [ ] ( [ ] { } ) always clobbers reg byte a +Statement [52] (byte~) vblank::$1 ← *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) & (byte) 1 [ vblank::$1 ] ( [ vblank::$1 ] { } ) always clobbers reg byte a +Statement [56] (byte~) vblank::$3 ← *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) & (byte) 1 [ vblank::$3 ] ( [ vblank::$3 ] { } ) always clobbers reg byte a +Statement [60] return [ ] ( [ ] { } ) always clobbers reg byte a reg byte x reg byte y +Statement asm { cld ldx#$ff txs } always clobbers reg byte x +Statement [5] *((byte*)(const struct RICOH_2C02*) PPU) ← (byte) 0 [ ] ( [ ] { } main:2 [ ] { } ) always clobbers reg byte a +Statement [6] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK) ← (byte) 0 [ ] ( [ ] { } main:2 [ ] { } ) always clobbers reg byte a +Statement [7] *((const nomodify byte*) FR_COUNTER) ← (byte) $40 [ ] ( [ ] { } main:2 [ ] { } ) always clobbers reg byte a +Statement [8] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_DMC_FREQ) ← (byte) $40 [ ] ( [ ] { } main:2 [ ] { } ) always clobbers reg byte a +Statement asm { ldaPPU_PPUSTATUS } always clobbers reg byte a +Statement [11] (byte~) main::waitForVBlank1_$0 ← *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) & (byte) $80 [ main::waitForVBlank1_$0 ] ( [ main::waitForVBlank1_$0 ] { } main:2 [ main::waitForVBlank1_$0 ] { } ) always clobbers reg byte a +Statement [14] *((const nomodify byte*) MEMORY + (byte) main::i#2) ← (byte) 0 [ main::i#2 ] ( [ main::i#2 ] { } main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [15] *((const nomodify byte*) MEMORY+(word) $100 + (byte) main::i#2) ← (byte) 0 [ main::i#2 ] ( [ main::i#2 ] { } main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [16] *((const nomodify byte*) MEMORY+(word) $200 + (byte) main::i#2) ← (byte) 0 [ main::i#2 ] ( [ main::i#2 ] { } main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [17] *((const nomodify byte*) MEMORY+(word) $300 + (byte) main::i#2) ← (byte) 0 [ main::i#2 ] ( [ main::i#2 ] { } main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [18] *((const nomodify byte*) MEMORY+(word) $400 + (byte) main::i#2) ← (byte) 0 [ main::i#2 ] ( [ main::i#2 ] { } main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [19] *((const nomodify byte*) MEMORY+(word) $500 + (byte) main::i#2) ← (byte) 0 [ main::i#2 ] ( [ main::i#2 ] { } main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [20] *((const nomodify byte*) MEMORY+(word) $600 + (byte) main::i#2) ← (byte) 0 [ main::i#2 ] ( [ main::i#2 ] { } main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [21] *((const nomodify byte*) MEMORY+(word) $700 + (byte) main::i#2) ← (byte) 0 [ main::i#2 ] ( [ main::i#2 ] { } main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [25] (byte~) main::waitForVBlank2_$0 ← *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) & (byte) $80 [ main::waitForVBlank2_$0 ] ( [ main::waitForVBlank2_$0 ] { } main:2 [ main::waitForVBlank2_$0 ] { } ) always clobbers reg byte a +Statement [31] *((byte*)(const struct RICOH_2C02*) PPU) ← (byte) $80 [ ] ( [ ] { } main:2 [ ] { } ) always clobbers reg byte a +Statement [32] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK) ← (byte) $10 [ ] ( [ ] { } main:2 [ ] { } ) always clobbers reg byte a +Statement [38] *((const nomodify byte*) OAM_BUFFER + (byte) initSpriteData::i#2) ← *((const byte*) SPRITES + (byte) initSpriteData::i#2) [ initSpriteData::i#2 ] ( initSpriteData:30 [ initSpriteData::i#2 ] { } main:2::initSpriteData:30 [ initSpriteData::i#2 ] { } ) always clobbers reg byte a +Statement asm { ldaPPU_PPUSTATUS } always clobbers reg byte a +Statement [41] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR) ← >(const nomodify byte*) PPU_PALETTE [ ] ( initPaletteData:28 [ ] { } main:2::initPaletteData:28 [ ] { } ) always clobbers reg byte a +Statement [42] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR) ← (byte) 0 [ ] ( initPaletteData:28 [ ] { } main:2::initPaletteData:28 [ ] { } ) always clobbers reg byte a +Statement [46] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUDATA) ← *((const byte*) PALETTE + (byte) initPaletteData::i#2) [ initPaletteData::i#2 ] ( initPaletteData:28 [ initPaletteData::i#2 ] { } main:2::initPaletteData:28 [ initPaletteData::i#2 ] { } ) always clobbers reg byte a +Statement [48] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_OAMADDR) ← (byte) 0 [ ] ( [ ] { } ) always clobbers reg byte a +Statement [49] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_OAMDMA) ← >(const nomodify byte*) OAM_BUFFER [ ] ( [ ] { } ) always clobbers reg byte a +Statement [50] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) ← (byte) 1 [ ] ( [ ] { } ) always clobbers reg byte a +Statement [51] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) ← (byte) 0 [ ] ( [ ] { } ) always clobbers reg byte a +Statement [52] (byte~) vblank::$1 ← *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) & (byte) 1 [ vblank::$1 ] ( [ vblank::$1 ] { } ) always clobbers reg byte a +Statement [56] (byte~) vblank::$3 ← *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) & (byte) 1 [ vblank::$3 ] ( [ vblank::$3 ] { } ) always clobbers reg byte a +Statement [60] return [ ] ( [ ] { } ) always clobbers reg byte a reg byte x reg byte y +Potential registers zp[1]:2 [ main::i#2 main::i#1 ] : zp[1]:2 , reg byte x , reg byte y , +Potential registers zp[1]:3 [ initSpriteData::i#2 initSpriteData::i#1 ] : zp[1]:3 , reg byte x , reg byte y , +Potential registers zp[1]:4 [ initPaletteData::i#2 initPaletteData::i#1 ] : zp[1]:4 , reg byte x , reg byte y , +Potential registers zp[1]:5 [ main::waitForVBlank1_$0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:6 [ main::waitForVBlank2_$0 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:7 [ vblank::$1 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:8 [ vblank::$3 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [initSpriteData] 3,670.33: zp[1]:3 [ initSpriteData::i#2 initSpriteData::i#1 ] +Uplift Scope [initPaletteData] 3,336.67: zp[1]:4 [ initPaletteData::i#2 initPaletteData::i#1 ] +Uplift Scope [main] 263.72: zp[1]:2 [ main::i#2 main::i#1 ] 202: zp[1]:5 [ main::waitForVBlank1_$0 ] 202: zp[1]:6 [ main::waitForVBlank2_$0 ] +Uplift Scope [vblank] 4: zp[1]:7 [ vblank::$1 ] 4: zp[1]:8 [ vblank::$3 ] +Uplift Scope [RICOH_2C02] +Uplift Scope [RICOH_2A03] +Uplift Scope [moveLuigiRight] +Uplift Scope [moveLuigiLeft] +Uplift Scope [] + +Uplifting [initSpriteData] best 2555 combination reg byte x [ initSpriteData::i#2 initSpriteData::i#1 ] +Uplifting [initPaletteData] best 2435 combination reg byte x [ initPaletteData::i#2 initPaletteData::i#1 ] +Uplifting [main] best 1985 combination reg byte x [ main::i#2 main::i#1 ] reg byte a [ main::waitForVBlank1_$0 ] reg byte a [ main::waitForVBlank2_$0 ] +Uplifting [vblank] best 1973 combination reg byte a [ vblank::$1 ] reg byte a [ vblank::$3 ] +Uplifting [RICOH_2C02] best 1973 combination +Uplifting [RICOH_2A03] best 1973 combination +Uplifting [moveLuigiRight] best 1973 combination +Uplifting [moveLuigiLeft] best 1973 combination +Uplifting [] best 1973 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// A minimal NES demo +// Based on: https://github.com/gregkrsak/first_nes written by Greg M. Krsak, 2018. + // Upstart + // Nintendo Entertainment System (NES) ROM +// https://sadistech.com/nesromtool/romdoc.html +// https://forums.nesdev.com/viewtopic.php?f=2&t=9896 +// https://github.com/gregkrsak/first_nes +.file [name="nes-demo.nes", type="bin", segments="NesRom"] +.file [name="nes-demo.nes_hdr", type="bin", segments="Header"] +.file [name="nes-demo.nes_prg", type="bin", segments="ProgramRom"] +.file [name="nes-demo.nes_chr", type="bin", segments="CharacterRom"] +.segmentdef Header [ start=$0000, min=$0000, max=$000f, fill ] +.segmentdef Tiles [ start=$0000, min=$0000, max=$1fff, fill ] +.segmentdef Code [ start=$c000, min=$c000, max=$fff9 ] +.segmentdef Data [ startAfter="Code", min=$c000, max=$fff9 ] +.segmentdef Vectors [ start=$fffa, min=$fffa, max=$ffff ] +.segmentdef ProgramRom [ segments="Code, Data, Vectors" ] +.segmentdef CharacterRom [ segments="Tiles" ] +.segmentdef NesRom +//.segment NesRom +//.segmentout [ segments="Header" ] +//.segmentout [ segments="ProgramRom" ] +//.segmentout [ segments="CharacterRom" ] +.segment Header +.text @"NES\$1a" +.byte $01 // 1x 16KB ROM (PRG) +.byte $01 // 1x 8KB VROM (CHR) +.byte %00000001 // Mapper nibble 0000 == No mapping (a simple 16KB PRG + 8KB CHR game) + // Mirroring nibble 0001 == Vertical mirroring only +.segment Code + + // Global Constants & labels + .const OFFSET_STRUCT_RICOH_2A03_DMC_FREQ = $10 + .const OFFSET_STRUCT_RICOH_2C02_PPUMASK = 1 + .const OFFSET_STRUCT_RICOH_2C02_PPUSTATUS = 2 + .const OFFSET_STRUCT_RICOH_2C02_OAMADDR = 3 + .const OFFSET_STRUCT_RICOH_2A03_OAMDMA = $14 + .const OFFSET_STRUCT_RICOH_2A03_JOY1 = $16 + .const OFFSET_STRUCT_RICOH_2C02_PPUADDR = 6 + .const OFFSET_STRUCT_RICOH_2C02_PPUDATA = 7 + .const SIZEOF_BYTE = 1 + // $3000-$3EFF $0F00 Mirrors of $2000-$2EFF + // $3F00-$3F1F $0020 Palette RAM indexes + .label PPU_PALETTE = $3f00 + // APU Frame Counter + // generates low-frequency clocks for the channels and an optional 60 Hz interrupt. + // https://wiki.nesdev.com/w/index.php/APU_Frame_Counter + // ------+-----+--------------------------------------------------------------- + // $4017 | W | FR_COUNTER Frame Counter Set mode and interrupt + // ------+-----+--------------------------------------------------------------- + // | 7 | Sequencer mode: 0 selects 4-step sequence, 1 selects 5-step sequence + // | 6 | Interrupt inhibit flag. If set, the frame interrupt flag is cleared, otherwise it is unaffected. + // ------+-----+--------------------------------------------------------------- + // Side effects After 3 or 4 CPU clock cycles*, the timer is reset. + // If the mode flag is set, then both "quarter frame" and "half frame" signals are also generated. + .label FR_COUNTER = $4017 + // Pointer to the start of RAM memory + .label MEMORY = 0 + // OAM (Object Attribute Memory) Buffer + // Will be transfered to the PPU via DMA + .label OAM_BUFFER = $200 + // PPU Status Register for reading in ASM + .label PPU_PPUSTATUS = $2002 + // NES Picture Processing Unit (PPU) + .label PPU = $2000 + // NES CPU and audion processing unit (APU) + .label APU = $4000 + // @begin +__bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +__b1_from___bbegin: + jmp __b1 + // @1 +__b1: + // [2] call main + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: +.segment Code + // main +// RESET Called when the NES is reset, including when it is turned on. +main: { + // asm { cld ldx#$ff txs } + cld + ldx #$ff + txs + jmp disableVideoOutput1 + // main::disableVideoOutput1 + disableVideoOutput1: + // [5] *((byte*)(const struct RICOH_2C02*) PPU) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta PPU + // [6] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUMASK + jmp disableAudioOutput1 + // main::disableAudioOutput1 + disableAudioOutput1: + // [7] *((const nomodify byte*) FR_COUNTER) ← (byte) $40 -- _deref_pbuc1=vbuc2 + lda #$40 + sta FR_COUNTER + // [8] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_DMC_FREQ) ← (byte) $40 -- _deref_pbuc1=vbuc2 + lda #$40 + sta APU+OFFSET_STRUCT_RICOH_2A03_DMC_FREQ + jmp clearVBlankFlag1 + // main::clearVBlankFlag1 + clearVBlankFlag1: + // asm { ldaPPU_PPUSTATUS } + lda PPU_PPUSTATUS + // [10] phi from main::clearVBlankFlag1 to main::waitForVBlank1 [phi:main::clearVBlankFlag1->main::waitForVBlank1] + waitForVBlank1_from_clearVBlankFlag1: + jmp waitForVBlank1 + // main::waitForVBlank1 + waitForVBlank1: + jmp waitForVBlank1___b1 + // main::waitForVBlank1_@1 + waitForVBlank1___b1: + // [11] (byte~) main::waitForVBlank1_$0 ← *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) & (byte) $80 -- vbuaa=_deref_pbuc1_band_vbuc2 + lda #$80 + and PPU+OFFSET_STRUCT_RICOH_2C02_PPUSTATUS + // [12] if((byte) 0==(byte~) main::waitForVBlank1_$0) goto main::waitForVBlank1_@1 -- vbuc1_eq_vbuaa_then_la1 + cmp #0 + beq waitForVBlank1___b1 + // [13] phi from main::waitForVBlank1_@1 to main::@1 [phi:main::waitForVBlank1_@1->main::@1] + __b1_from_waitForVBlank1___b1: + // [13] phi (byte) main::i#2 = (byte) 0 [phi:main::waitForVBlank1_@1->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp __b1 + // [13] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + __b1_from___b1: + // [13] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#0] -- register_copy + jmp __b1 + // main::@1 + __b1: + // [14] *((const nomodify byte*) MEMORY + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuxx=vbuc2 + lda #0 + sta MEMORY,x + // [15] *((const nomodify byte*) MEMORY+(word) $100 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuxx=vbuc2 + lda #0 + sta MEMORY+$100,x + // [16] *((const nomodify byte*) MEMORY+(word) $200 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuxx=vbuc2 + lda #0 + sta MEMORY+$200,x + // [17] *((const nomodify byte*) MEMORY+(word) $300 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuxx=vbuc2 + lda #0 + sta MEMORY+$300,x + // [18] *((const nomodify byte*) MEMORY+(word) $400 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuxx=vbuc2 + lda #0 + sta MEMORY+$400,x + // [19] *((const nomodify byte*) MEMORY+(word) $500 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuxx=vbuc2 + lda #0 + sta MEMORY+$500,x + // [20] *((const nomodify byte*) MEMORY+(word) $600 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuxx=vbuc2 + lda #0 + sta MEMORY+$600,x + // [21] *((const nomodify byte*) MEMORY+(word) $700 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuxx=vbuc2 + lda #0 + sta MEMORY+$700,x + // [22] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + // [23] if((byte) 0!=(byte) main::i#1) goto main::@1 -- vbuc1_neq_vbuxx_then_la1 + cpx #0 + bne __b1_from___b1 + // [24] phi from main::@1 to main::waitForVBlank2 [phi:main::@1->main::waitForVBlank2] + waitForVBlank2_from___b1: + jmp waitForVBlank2 + // main::waitForVBlank2 + waitForVBlank2: + jmp waitForVBlank2___b1 + // main::waitForVBlank2_@1 + waitForVBlank2___b1: + // [25] (byte~) main::waitForVBlank2_$0 ← *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) & (byte) $80 -- vbuaa=_deref_pbuc1_band_vbuc2 + lda #$80 + and PPU+OFFSET_STRUCT_RICOH_2C02_PPUSTATUS + // [26] if((byte) 0==(byte~) main::waitForVBlank2_$0) goto main::waitForVBlank2_@1 -- vbuc1_eq_vbuaa_then_la1 + cmp #0 + beq waitForVBlank2___b1 + // [27] phi from main::waitForVBlank2_@1 to main::@3 [phi:main::waitForVBlank2_@1->main::@3] + __b3_from_waitForVBlank2___b1: + jmp __b3 + // main::@3 + __b3: + // [28] call initPaletteData + // Now the PPU is ready. + jsr initPaletteData + // [29] phi from main::@3 to main::@4 [phi:main::@3->main::@4] + __b4_from___b3: + jmp __b4 + // main::@4 + __b4: + // [30] call initSpriteData + // [34] phi from main::@4 to initSpriteData [phi:main::@4->initSpriteData] + initSpriteData_from___b4: + jsr initSpriteData + jmp enableVideoOutput1 + // main::enableVideoOutput1 + enableVideoOutput1: + // [31] *((byte*)(const struct RICOH_2C02*) PPU) ← (byte) $80 -- _deref_pbuc1=vbuc2 + lda #$80 + sta PPU + // [32] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK) ← (byte) $10 -- _deref_pbuc1=vbuc2 + lda #$10 + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUMASK + // [33] phi from main::@2 main::enableVideoOutput1 to main::@2 [phi:main::@2/main::enableVideoOutput1->main::@2] + __b2_from___b2: + __b2_from_enableVideoOutput1: + jmp __b2 + // Infinite loop + // main::@2 + __b2: + jmp __b2_from___b2 +} + // initSpriteData +// Initialize OAM (Object Attribute Memory) Buffer +initSpriteData: { + // [35] phi from initSpriteData to initSpriteData::@1 [phi:initSpriteData->initSpriteData::@1] + __b1_from_initSpriteData: + // [35] phi (byte) initSpriteData::i#2 = (byte) 0 [phi:initSpriteData->initSpriteData::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp __b1 + // initSpriteData::@1 + __b1: + // [36] if((byte) initSpriteData::i#2<(byte) $10*(const byte) SIZEOF_BYTE) goto initSpriteData::@2 -- vbuxx_lt_vbuc1_then_la1 + cpx #$10*SIZEOF_BYTE + bcc __b2 + jmp __breturn + // initSpriteData::@return + __breturn: + // [37] return + rts + // initSpriteData::@2 + __b2: + // [38] *((const nomodify byte*) OAM_BUFFER + (byte) initSpriteData::i#2) ← *((const byte*) SPRITES + (byte) initSpriteData::i#2) -- pbuc1_derefidx_vbuxx=pbuc2_derefidx_vbuxx + lda SPRITES,x + sta OAM_BUFFER,x + // [39] (byte) initSpriteData::i#1 ← ++ (byte) initSpriteData::i#2 -- vbuxx=_inc_vbuxx + inx + // [35] phi from initSpriteData::@2 to initSpriteData::@1 [phi:initSpriteData::@2->initSpriteData::@1] + __b1_from___b2: + // [35] phi (byte) initSpriteData::i#2 = (byte) initSpriteData::i#1 [phi:initSpriteData::@2->initSpriteData::@1#0] -- register_copy + jmp __b1 +} + // initPaletteData +// Copy palette values to PPU +initPaletteData: { + // asm { ldaPPU_PPUSTATUS } + // Reset the high/low latch to "high" + lda PPU_PPUSTATUS + // [41] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR) ← >(const nomodify byte*) PPU_PALETTE -- _deref_pbuc1=vbuc2 + // Write the high byte of PPU Palette address + lda #>PPU_PALETTE + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUADDR + // [42] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // Write the low byte of PPU Palette address + lda #0 + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUADDR + // [43] phi from initPaletteData to initPaletteData::@1 [phi:initPaletteData->initPaletteData::@1] + __b1_from_initPaletteData: + // [43] phi (byte) initPaletteData::i#2 = (byte) 0 [phi:initPaletteData->initPaletteData::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp __b1 + // Write to PPU + // initPaletteData::@1 + __b1: + // [44] if((byte) initPaletteData::i#2<(byte) $20*(const byte) SIZEOF_BYTE) goto initPaletteData::@2 -- vbuxx_lt_vbuc1_then_la1 + cpx #$20*SIZEOF_BYTE + bcc __b2 + jmp __breturn + // initPaletteData::@return + __breturn: + // [45] return + rts + // initPaletteData::@2 + __b2: + // [46] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUDATA) ← *((const byte*) PALETTE + (byte) initPaletteData::i#2) -- _deref_pbuc1=pbuc2_derefidx_vbuxx + lda PALETTE,x + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUDATA + // [47] (byte) initPaletteData::i#1 ← ++ (byte) initPaletteData::i#2 -- vbuxx=_inc_vbuxx + inx + // [43] phi from initPaletteData::@2 to initPaletteData::@1 [phi:initPaletteData::@2->initPaletteData::@1] + __b1_from___b2: + // [43] phi (byte) initPaletteData::i#2 = (byte) initPaletteData::i#1 [phi:initPaletteData::@2->initPaletteData::@1#0] -- register_copy + jmp __b1 +} + // vblank +// NMI Called when the PPU refreshes the screen (also known as the V-Blank period) +vblank: { + // entry interrupt(HARDWARE_STACK) + pha + txa + pha + tya + pha + // [48] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_OAMADDR) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // Refresh DRAM-stored sprite data before it decays. + // Set OAM start address to sprite#0 + lda #0 + sta PPU+OFFSET_STRUCT_RICOH_2C02_OAMADDR + // [49] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_OAMDMA) ← >(const nomodify byte*) OAM_BUFFER -- _deref_pbuc1=vbuc2 + // Set the high byte (02) of the RAM address and start the DMA transfer to OAM memory + lda #>OAM_BUFFER + sta APU+OFFSET_STRUCT_RICOH_2A03_OAMDMA + // [50] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) ← (byte) 1 -- _deref_pbuc1=vbuc2 + // Freeze the button positions. + lda #1 + sta APU+OFFSET_STRUCT_RICOH_2A03_JOY1 + // [51] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta APU+OFFSET_STRUCT_RICOH_2A03_JOY1 + // [52] (byte~) vblank::$1 ← *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) & (byte) 1 -- vbuaa=_deref_pbuc1_band_vbuc2 + lda #1 + and APU+OFFSET_STRUCT_RICOH_2A03_JOY1 + // [53] if((byte) 0==(byte~) vblank::$1) goto vblank::@1 -- vbuc1_eq_vbuaa_then_la1 + cmp #0 + beq __b1 + // [54] phi from vblank to vblank::@2 [phi:vblank->vblank::@2] + __b2_from_vblank: + jmp __b2 + // vblank::@2 + __b2: + // [55] call moveLuigiRight + jsr moveLuigiRight + jmp __b1 + // vblank::@1 + __b1: + // [56] (byte~) vblank::$3 ← *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) & (byte) 1 -- vbuaa=_deref_pbuc1_band_vbuc2 + lda #1 + and APU+OFFSET_STRUCT_RICOH_2A03_JOY1 + // [57] if((byte) 0==(byte~) vblank::$3) goto vblank::@return -- vbuc1_eq_vbuaa_then_la1 + cmp #0 + beq __breturn + // [58] phi from vblank::@1 to vblank::@3 [phi:vblank::@1->vblank::@3] + __b3_from___b1: + jmp __b3 + // vblank::@3 + __b3: + // [59] call moveLuigiLeft + jsr moveLuigiLeft + jmp __breturn + // vblank::@return + __breturn: + // [60] return - exit interrupt(HARDWARE_STACK) + pla + tay + pla + tax + pla + rti +} + // moveLuigiLeft +// move the Luigi sprites left +moveLuigiLeft: { + // [61] *((const nomodify byte*) OAM_BUFFER+(byte) 3) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) 3) -- _deref_pbuc1=_dec__deref_pbuc1 + dec OAM_BUFFER+3 + // [62] *((const nomodify byte*) OAM_BUFFER+(byte) 7) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) 7) -- _deref_pbuc1=_dec__deref_pbuc1 + dec OAM_BUFFER+7 + // [63] *((const nomodify byte*) OAM_BUFFER+(byte) $b) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) $b) -- _deref_pbuc1=_dec__deref_pbuc1 + dec OAM_BUFFER+$b + // [64] *((const nomodify byte*) OAM_BUFFER+(byte) $f) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) $f) -- _deref_pbuc1=_dec__deref_pbuc1 + dec OAM_BUFFER+$f + jmp __breturn + // moveLuigiLeft::@return + __breturn: + // [65] return + rts +} + // moveLuigiRight +// move the Luigi sprites right +moveLuigiRight: { + // [66] *((const nomodify byte*) OAM_BUFFER+(byte) 3) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) 3) -- _deref_pbuc1=_inc__deref_pbuc1 + inc OAM_BUFFER+3 + // [67] *((const nomodify byte*) OAM_BUFFER+(byte) 7) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) 7) -- _deref_pbuc1=_inc__deref_pbuc1 + inc OAM_BUFFER+7 + // [68] *((const nomodify byte*) OAM_BUFFER+(byte) $b) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) $b) -- _deref_pbuc1=_inc__deref_pbuc1 + inc OAM_BUFFER+$b + // [69] *((const nomodify byte*) OAM_BUFFER+(byte) $f) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) $f) -- _deref_pbuc1=_inc__deref_pbuc1 + inc OAM_BUFFER+$f + jmp __breturn + // moveLuigiRight::@return + __breturn: + // [70] return + rts +} + // File Data +.segment Data + PALETTE: .byte $f, $31, $32, $33, $f, $35, $36, $37, $f, $39, $3a, $3b, $f, $3d, $3e, $f, $f, $1c, $15, $14, $f, 2, $38, $3c, $f, $30, $37, $1a, $f, $f, $f, $f + // Small Luigi Sprite Data + SPRITES: .byte $80, $36, 2, $80, $80, $37, 2, $88, $88, $38, 2, $80, $88, $39, 2, $88 +.segment Tiles +TILES: +.import binary "smb1_chr.bin" + +.segment Vectors + VECTORS: .word vblank, main, 0 +.segment NesRom +NES_ROM: +.segmentout [ segments="Header" ] +.segmentout [ segments="ProgramRom" ] +.segmentout [ segments="CharacterRom" ] + + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __bend +Removing instruction jmp disableVideoOutput1 +Removing instruction jmp disableAudioOutput1 +Removing instruction jmp clearVBlankFlag1 +Removing instruction jmp waitForVBlank1 +Removing instruction jmp waitForVBlank1___b1 +Removing instruction jmp __b1 +Removing instruction jmp waitForVBlank2 +Removing instruction jmp waitForVBlank2___b1 +Removing instruction jmp __b3 +Removing instruction jmp __b4 +Removing instruction jmp enableVideoOutput1 +Removing instruction jmp __b2 +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __b2 +Removing instruction jmp __b1 +Removing instruction jmp __b3 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction lda #0 +Removing instruction lda #$40 +Removing instruction lda #0 +Removing instruction lda #0 +Removing instruction lda #0 +Removing instruction lda #0 +Removing instruction lda #0 +Removing instruction lda #0 +Removing instruction lda #0 +Succesful ASM optimization Pass5UnnecesaryLoadElimination +Replacing label waitForVBlank1___b1 with waitForVBlank1 +Replacing label __b1_from___b1 with __b1 +Replacing label waitForVBlank2___b1 with waitForVBlank2 +Removing instruction __b1_from___bbegin: +Removing instruction __b1: +Removing instruction __bend_from___b1: +Removing instruction waitForVBlank1_from_clearVBlankFlag1: +Removing instruction waitForVBlank1___b1: +Removing instruction __b1_from___b1: +Removing instruction waitForVBlank2_from___b1: +Removing instruction waitForVBlank2___b1: +Removing instruction __b3_from_waitForVBlank2___b1: +Removing instruction __b4_from___b3: +Removing instruction initSpriteData_from___b4: +Removing instruction __b2_from_enableVideoOutput1: +Removing instruction __b2_from_vblank: +Removing instruction __b3_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __bbegin: +Removing instruction __bend: +Removing instruction disableVideoOutput1: +Removing instruction disableAudioOutput1: +Removing instruction clearVBlankFlag1: +Removing instruction __b1_from_waitForVBlank1___b1: +Removing instruction __b3: +Removing instruction __b4: +Removing instruction enableVideoOutput1: +Removing instruction __b2: +Removing instruction __b1_from_initSpriteData: +Removing instruction __breturn: +Removing instruction __b1_from___b2: +Removing instruction __b1_from_initPaletteData: +Removing instruction __breturn: +Removing instruction __b1_from___b2: +Removing instruction __b2: +Removing instruction __b3: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Relabelling long label __b2_from___b2 to __b2 +Succesful ASM optimization Pass5RelabelLongLabels +Removing instruction jmp __b1 +Succesful ASM optimization Pass5NextJumpElimination +Replacing instruction ldx #0 with TAX + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(const struct RICOH_2A03*) APU = (struct RICOH_2A03*) 16384 +(const nomodify byte*) FR_COUNTER = (byte*) 16407 +(const nomodify byte*) MEMORY = (byte*) 0 +(const byte*) NES_ROM[] = kickasm {{ .segmentout [ segments="Header" ] +.segmentout [ segments="ProgramRom" ] +.segmentout [ segments="CharacterRom" ] + }} +(const nomodify byte*) OAM_BUFFER = (byte*) 512 +(const byte) OFFSET_STRUCT_RICOH_2A03_DMC_FREQ = (byte) $10 +(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1 = (byte) $16 +(const byte) OFFSET_STRUCT_RICOH_2A03_OAMDMA = (byte) $14 +(const byte) OFFSET_STRUCT_RICOH_2C02_OAMADDR = (byte) 3 +(const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR = (byte) 6 +(const byte) OFFSET_STRUCT_RICOH_2C02_PPUDATA = (byte) 7 +(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK = (byte) 1 +(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS = (byte) 2 +(const byte*) PALETTE[(number) $20] = { (byte) $f, (byte) $31, (byte) $32, (byte) $33, (byte) $f, (byte) $35, (byte) $36, (byte) $37, (byte) $f, (byte) $39, (byte) $3a, (byte) $3b, (byte) $f, (byte) $3d, (byte) $3e, (byte) $f, (byte) $f, (byte) $1c, (byte) $15, (byte) $14, (byte) $f, (byte) 2, (byte) $38, (byte) $3c, (byte) $f, (byte) $30, (byte) $37, (byte) $1a, (byte) $f, (byte) $f, (byte) $f, (byte) $f } +(const struct RICOH_2C02*) PPU = (struct RICOH_2C02*) 8192 +(const nomodify byte*) PPU_PALETTE = (byte*) 16128 +(const to_volatile byte*) PPU_PPUSTATUS = (byte*) 8194 +(byte) RICOH_2A03::DMC_FREQ +(byte) RICOH_2A03::DMC_LEN +(byte) RICOH_2A03::DMC_RAW +(byte) RICOH_2A03::DMC_START +(byte) RICOH_2A03::JOY1 +(byte) RICOH_2A03::JOY2 +(byte) RICOH_2A03::NOISE_HI +(byte) RICOH_2A03::NOISE_LO +(byte) RICOH_2A03::NOISE_VOL +(byte) RICOH_2A03::OAMDMA +(byte) RICOH_2A03::SND_CHN +(byte) RICOH_2A03::SQ1_HI +(byte) RICOH_2A03::SQ1_LO +(byte) RICOH_2A03::SQ1_SWEEP +(byte) RICOH_2A03::SQ1_VOL +(byte) RICOH_2A03::SQ2_HI +(byte) RICOH_2A03::SQ2_LO +(byte) RICOH_2A03::SQ2_SWEEP +(byte) RICOH_2A03::SQ2_VOL +(byte) RICOH_2A03::TRI_HI +(byte) RICOH_2A03::TRI_LINEAR +(byte) RICOH_2A03::TRI_LO +(byte) RICOH_2A03::UNUSED1 +(byte) RICOH_2A03::UNUSED2 +(byte) RICOH_2C02::OAMADDR +(byte) RICOH_2C02::OAMDATA +(byte) RICOH_2C02::PPUADDR +(byte) RICOH_2C02::PPUCTRL +(byte) RICOH_2C02::PPUDATA +(byte) RICOH_2C02::PPUMASK +(byte) RICOH_2C02::PPUSCROLL +(volatile byte) RICOH_2C02::PPUSTATUS loadstore +(const byte) SIZEOF_BYTE = (byte) 1 +(const byte*) SPRITES[] = { (byte) $80, (byte) $36, (byte) 2, (byte) $80, (byte) $80, (byte) $37, (byte) 2, (byte) $88, (byte) $88, (byte) $38, (byte) 2, (byte) $80, (byte) $88, (byte) $39, (byte) 2, (byte) $88 } +(const byte*) TILES[] = kickasm {{ .import binary "smb1_chr.bin" + }} +(const to_nomodify void()**) VECTORS[] = { &interrupt(HARDWARE_STACK)(void()) vblank(), &(void()) main(), (void()*) 0 } +(void()) initPaletteData() +(label) initPaletteData::@1 +(label) initPaletteData::@2 +(label) initPaletteData::@return +(byte) initPaletteData::i +(byte) initPaletteData::i#1 reg byte x 2002.0 +(byte) initPaletteData::i#2 reg byte x 1334.6666666666667 +(void()) initSpriteData() +(label) initSpriteData::@1 +(label) initSpriteData::@2 +(label) initSpriteData::@return +(byte) initSpriteData::i +(byte) initSpriteData::i#1 reg byte x 2002.0 +(byte) initSpriteData::i#2 reg byte x 1668.3333333333335 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::clearVBlankFlag1 +(label) main::disableAudioOutput1 +(label) main::disableVideoOutput1 +(label) main::enableVideoOutput1 +(byte) main::i +(byte) main::i#1 reg byte x 151.5 +(byte) main::i#2 reg byte x 112.22222222222223 +(label) main::waitForVBlank1 +(byte~) main::waitForVBlank1_$0 reg byte a 202.0 +(label) main::waitForVBlank1_@1 +(label) main::waitForVBlank2 +(byte~) main::waitForVBlank2_$0 reg byte a 202.0 +(label) main::waitForVBlank2_@1 +(void()) moveLuigiLeft() +(label) moveLuigiLeft::@return +(void()) moveLuigiRight() +(label) moveLuigiRight::@return +interrupt(HARDWARE_STACK)(void()) vblank() +(byte~) vblank::$1 reg byte a 4.0 +(byte~) vblank::$3 reg byte a 4.0 +(label) vblank::@1 +(label) vblank::@2 +(label) vblank::@3 +(label) vblank::@return + +reg byte x [ main::i#2 main::i#1 ] +reg byte x [ initSpriteData::i#2 initSpriteData::i#1 ] +reg byte x [ initPaletteData::i#2 initPaletteData::i#1 ] +reg byte a [ main::waitForVBlank1_$0 ] +reg byte a [ main::waitForVBlank2_$0 ] +reg byte a [ vblank::$1 ] +reg byte a [ vblank::$3 ] + + +FINAL ASSEMBLER +Score: 1505 + + // File Comments +// A minimal NES demo +// Based on: https://github.com/gregkrsak/first_nes written by Greg M. Krsak, 2018. + // Upstart + // Nintendo Entertainment System (NES) ROM +// https://sadistech.com/nesromtool/romdoc.html +// https://forums.nesdev.com/viewtopic.php?f=2&t=9896 +// https://github.com/gregkrsak/first_nes +.file [name="nes-demo.nes", type="bin", segments="NesRom"] +.file [name="nes-demo.nes_hdr", type="bin", segments="Header"] +.file [name="nes-demo.nes_prg", type="bin", segments="ProgramRom"] +.file [name="nes-demo.nes_chr", type="bin", segments="CharacterRom"] +.segmentdef Header [ start=$0000, min=$0000, max=$000f, fill ] +.segmentdef Tiles [ start=$0000, min=$0000, max=$1fff, fill ] +.segmentdef Code [ start=$c000, min=$c000, max=$fff9 ] +.segmentdef Data [ startAfter="Code", min=$c000, max=$fff9 ] +.segmentdef Vectors [ start=$fffa, min=$fffa, max=$ffff ] +.segmentdef ProgramRom [ segments="Code, Data, Vectors" ] +.segmentdef CharacterRom [ segments="Tiles" ] +.segmentdef NesRom +//.segment NesRom +//.segmentout [ segments="Header" ] +//.segmentout [ segments="ProgramRom" ] +//.segmentout [ segments="CharacterRom" ] +.segment Header +.text @"NES\$1a" +.byte $01 // 1x 16KB ROM (PRG) +.byte $01 // 1x 8KB VROM (CHR) +.byte %00000001 // Mapper nibble 0000 == No mapping (a simple 16KB PRG + 8KB CHR game) + // Mirroring nibble 0001 == Vertical mirroring only +.segment Code + + // Global Constants & labels + .const OFFSET_STRUCT_RICOH_2A03_DMC_FREQ = $10 + .const OFFSET_STRUCT_RICOH_2C02_PPUMASK = 1 + .const OFFSET_STRUCT_RICOH_2C02_PPUSTATUS = 2 + .const OFFSET_STRUCT_RICOH_2C02_OAMADDR = 3 + .const OFFSET_STRUCT_RICOH_2A03_OAMDMA = $14 + .const OFFSET_STRUCT_RICOH_2A03_JOY1 = $16 + .const OFFSET_STRUCT_RICOH_2C02_PPUADDR = 6 + .const OFFSET_STRUCT_RICOH_2C02_PPUDATA = 7 + .const SIZEOF_BYTE = 1 + // $3000-$3EFF $0F00 Mirrors of $2000-$2EFF + // $3F00-$3F1F $0020 Palette RAM indexes + .label PPU_PALETTE = $3f00 + // APU Frame Counter + // generates low-frequency clocks for the channels and an optional 60 Hz interrupt. + // https://wiki.nesdev.com/w/index.php/APU_Frame_Counter + // ------+-----+--------------------------------------------------------------- + // $4017 | W | FR_COUNTER Frame Counter Set mode and interrupt + // ------+-----+--------------------------------------------------------------- + // | 7 | Sequencer mode: 0 selects 4-step sequence, 1 selects 5-step sequence + // | 6 | Interrupt inhibit flag. If set, the frame interrupt flag is cleared, otherwise it is unaffected. + // ------+-----+--------------------------------------------------------------- + // Side effects After 3 or 4 CPU clock cycles*, the timer is reset. + // If the mode flag is set, then both "quarter frame" and "half frame" signals are also generated. + .label FR_COUNTER = $4017 + // Pointer to the start of RAM memory + .label MEMORY = 0 + // OAM (Object Attribute Memory) Buffer + // Will be transfered to the PPU via DMA + .label OAM_BUFFER = $200 + // PPU Status Register for reading in ASM + .label PPU_PPUSTATUS = $2002 + // NES Picture Processing Unit (PPU) + .label PPU = $2000 + // NES CPU and audion processing unit (APU) + .label APU = $4000 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [3] phi from @1 to @end [phi:@1->@end] + // @end +.segment Code + // main +// RESET Called when the NES is reset, including when it is turned on. +main: { + // asm + // asm { cld ldx#$ff txs } + cld + ldx #$ff + txs + // main::disableVideoOutput1 + // PPU->PPUCTRL = 0 + // [5] *((byte*)(const struct RICOH_2C02*) PPU) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta PPU + // PPU->PPUMASK = 0 + // [6] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK) ← (byte) 0 -- _deref_pbuc1=vbuc2 + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUMASK + // main::disableAudioOutput1 + // *FR_COUNTER = 0b01000000 + // [7] *((const nomodify byte*) FR_COUNTER) ← (byte) $40 -- _deref_pbuc1=vbuc2 + lda #$40 + sta FR_COUNTER + // APU->DMC_FREQ = 0b01000000 + // [8] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_DMC_FREQ) ← (byte) $40 -- _deref_pbuc1=vbuc2 + sta APU+OFFSET_STRUCT_RICOH_2A03_DMC_FREQ + // main::clearVBlankFlag1 + // asm + // asm { ldaPPU_PPUSTATUS } + lda PPU_PPUSTATUS + // [10] phi from main::clearVBlankFlag1 to main::waitForVBlank1 [phi:main::clearVBlankFlag1->main::waitForVBlank1] + // main::waitForVBlank1 + waitForVBlank1: + // main::waitForVBlank1_@1 + // PPU->PPUSTATUS&0x80 + // [11] (byte~) main::waitForVBlank1_$0 ← *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) & (byte) $80 -- vbuaa=_deref_pbuc1_band_vbuc2 + lda #$80 + and PPU+OFFSET_STRUCT_RICOH_2C02_PPUSTATUS + // while(!(PPU->PPUSTATUS&0x80)) + // [12] if((byte) 0==(byte~) main::waitForVBlank1_$0) goto main::waitForVBlank1_@1 -- vbuc1_eq_vbuaa_then_la1 + cmp #0 + beq waitForVBlank1 + // [13] phi from main::waitForVBlank1_@1 to main::@1 [phi:main::waitForVBlank1_@1->main::@1] + // [13] phi (byte) main::i#2 = (byte) 0 [phi:main::waitForVBlank1_@1->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + // [13] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + // [13] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#0] -- register_copy + // main::@1 + __b1: + // (MEMORY+0x000)[i] = 0 + // [14] *((const nomodify byte*) MEMORY + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuxx=vbuc2 + lda #0 + sta MEMORY,x + // (MEMORY+0x100)[i] = 0 + // [15] *((const nomodify byte*) MEMORY+(word) $100 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuxx=vbuc2 + sta MEMORY+$100,x + // (MEMORY+0x200)[i] = 0 + // [16] *((const nomodify byte*) MEMORY+(word) $200 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuxx=vbuc2 + sta MEMORY+$200,x + // (MEMORY+0x300)[i] = 0 + // [17] *((const nomodify byte*) MEMORY+(word) $300 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuxx=vbuc2 + sta MEMORY+$300,x + // (MEMORY+0x400)[i] = 0 + // [18] *((const nomodify byte*) MEMORY+(word) $400 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuxx=vbuc2 + sta MEMORY+$400,x + // (MEMORY+0x500)[i] = 0 + // [19] *((const nomodify byte*) MEMORY+(word) $500 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuxx=vbuc2 + sta MEMORY+$500,x + // (MEMORY+0x600)[i] = 0 + // [20] *((const nomodify byte*) MEMORY+(word) $600 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuxx=vbuc2 + sta MEMORY+$600,x + // (MEMORY+0x700)[i] = 0 + // [21] *((const nomodify byte*) MEMORY+(word) $700 + (byte) main::i#2) ← (byte) 0 -- pbuc1_derefidx_vbuxx=vbuc2 + sta MEMORY+$700,x + // while (++i) + // [22] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + // [23] if((byte) 0!=(byte) main::i#1) goto main::@1 -- vbuc1_neq_vbuxx_then_la1 + cpx #0 + bne __b1 + // [24] phi from main::@1 to main::waitForVBlank2 [phi:main::@1->main::waitForVBlank2] + // main::waitForVBlank2 + waitForVBlank2: + // main::waitForVBlank2_@1 + // PPU->PPUSTATUS&0x80 + // [25] (byte~) main::waitForVBlank2_$0 ← *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS) & (byte) $80 -- vbuaa=_deref_pbuc1_band_vbuc2 + lda #$80 + and PPU+OFFSET_STRUCT_RICOH_2C02_PPUSTATUS + // while(!(PPU->PPUSTATUS&0x80)) + // [26] if((byte) 0==(byte~) main::waitForVBlank2_$0) goto main::waitForVBlank2_@1 -- vbuc1_eq_vbuaa_then_la1 + cmp #0 + beq waitForVBlank2 + // [27] phi from main::waitForVBlank2_@1 to main::@3 [phi:main::waitForVBlank2_@1->main::@3] + // main::@3 + // initPaletteData() + // [28] call initPaletteData + // Now the PPU is ready. + jsr initPaletteData + // [29] phi from main::@3 to main::@4 [phi:main::@3->main::@4] + // main::@4 + // initSpriteData() + // [30] call initSpriteData + // [34] phi from main::@4 to initSpriteData [phi:main::@4->initSpriteData] + jsr initSpriteData + // main::enableVideoOutput1 + // PPU->PPUCTRL = 0b10000000 + // [31] *((byte*)(const struct RICOH_2C02*) PPU) ← (byte) $80 -- _deref_pbuc1=vbuc2 + lda #$80 + sta PPU + // PPU->PPUMASK = 0b00010000 + // [32] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK) ← (byte) $10 -- _deref_pbuc1=vbuc2 + lda #$10 + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUMASK + // [33] phi from main::@2 main::enableVideoOutput1 to main::@2 [phi:main::@2/main::enableVideoOutput1->main::@2] + __b2: + // Infinite loop + // main::@2 + jmp __b2 +} + // initSpriteData +// Initialize OAM (Object Attribute Memory) Buffer +initSpriteData: { + // [35] phi from initSpriteData to initSpriteData::@1 [phi:initSpriteData->initSpriteData::@1] + // [35] phi (byte) initSpriteData::i#2 = (byte) 0 [phi:initSpriteData->initSpriteData::@1#0] -- vbuxx=vbuc1 + ldx #0 + // initSpriteData::@1 + __b1: + // for(char i=0;iinitSpriteData::@1] + // [35] phi (byte) initSpriteData::i#2 = (byte) initSpriteData::i#1 [phi:initSpriteData::@2->initSpriteData::@1#0] -- register_copy + jmp __b1 +} + // initPaletteData +// Copy palette values to PPU +initPaletteData: { + // asm + // asm { ldaPPU_PPUSTATUS } + // Reset the high/low latch to "high" + lda PPU_PPUSTATUS + // PPU->PPUADDR = >PPU_PALETTE + // [41] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR) ← >(const nomodify byte*) PPU_PALETTE -- _deref_pbuc1=vbuc2 + // Write the high byte of PPU Palette address + lda #>PPU_PALETTE + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUADDR + // PPU->PPUADDR = initPaletteData::@1] + // [43] phi (byte) initPaletteData::i#2 = (byte) 0 [phi:initPaletteData->initPaletteData::@1#0] -- vbuxx=vbuc1 + tax + // Write to PPU + // initPaletteData::@1 + __b1: + // for(char i=0;iPPUDATA = PALETTE[i] + // [46] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_PPUDATA) ← *((const byte*) PALETTE + (byte) initPaletteData::i#2) -- _deref_pbuc1=pbuc2_derefidx_vbuxx + lda PALETTE,x + sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUDATA + // for(char i=0;iinitPaletteData::@1] + // [43] phi (byte) initPaletteData::i#2 = (byte) initPaletteData::i#1 [phi:initPaletteData::@2->initPaletteData::@1#0] -- register_copy + jmp __b1 +} + // vblank +// NMI Called when the PPU refreshes the screen (also known as the V-Blank period) +vblank: { + // entry interrupt(HARDWARE_STACK) + pha + txa + pha + tya + pha + // PPU->OAMADDR = 0 + // [48] *((byte*)(const struct RICOH_2C02*) PPU+(const byte) OFFSET_STRUCT_RICOH_2C02_OAMADDR) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // Refresh DRAM-stored sprite data before it decays. + // Set OAM start address to sprite#0 + lda #0 + sta PPU+OFFSET_STRUCT_RICOH_2C02_OAMADDR + // APU->OAMDMA = >OAM_BUFFER + // [49] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_OAMDMA) ← >(const nomodify byte*) OAM_BUFFER -- _deref_pbuc1=vbuc2 + // Set the high byte (02) of the RAM address and start the DMA transfer to OAM memory + lda #>OAM_BUFFER + sta APU+OFFSET_STRUCT_RICOH_2A03_OAMDMA + // APU->JOY1 = 1 + // [50] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) ← (byte) 1 -- _deref_pbuc1=vbuc2 + // Freeze the button positions. + lda #1 + sta APU+OFFSET_STRUCT_RICOH_2A03_JOY1 + // APU->JOY1 = 0 + // [51] *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta APU+OFFSET_STRUCT_RICOH_2A03_JOY1 + // APU->JOY1&0b00000001 + // [52] (byte~) vblank::$1 ← *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) & (byte) 1 -- vbuaa=_deref_pbuc1_band_vbuc2 + lda #1 + and APU+OFFSET_STRUCT_RICOH_2A03_JOY1 + // if(APU->JOY1&0b00000001) + // [53] if((byte) 0==(byte~) vblank::$1) goto vblank::@1 -- vbuc1_eq_vbuaa_then_la1 + cmp #0 + beq __b1 + // [54] phi from vblank to vblank::@2 [phi:vblank->vblank::@2] + // vblank::@2 + // moveLuigiRight() + // [55] call moveLuigiRight + jsr moveLuigiRight + // vblank::@1 + __b1: + // APU->JOY1&0b00000001 + // [56] (byte~) vblank::$3 ← *((byte*)(const struct RICOH_2A03*) APU+(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1) & (byte) 1 -- vbuaa=_deref_pbuc1_band_vbuc2 + lda #1 + and APU+OFFSET_STRUCT_RICOH_2A03_JOY1 + // if(APU->JOY1&0b00000001) + // [57] if((byte) 0==(byte~) vblank::$3) goto vblank::@return -- vbuc1_eq_vbuaa_then_la1 + cmp #0 + beq __breturn + // [58] phi from vblank::@1 to vblank::@3 [phi:vblank::@1->vblank::@3] + // vblank::@3 + // moveLuigiLeft() + // [59] call moveLuigiLeft + jsr moveLuigiLeft + // vblank::@return + __breturn: + // } + // [60] return - exit interrupt(HARDWARE_STACK) + pla + tay + pla + tax + pla + rti +} + // moveLuigiLeft +// move the Luigi sprites left +moveLuigiLeft: { + // OAM_BUFFER[0x03]--; + // [61] *((const nomodify byte*) OAM_BUFFER+(byte) 3) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) 3) -- _deref_pbuc1=_dec__deref_pbuc1 + dec OAM_BUFFER+3 + // OAM_BUFFER[0x07]--; + // [62] *((const nomodify byte*) OAM_BUFFER+(byte) 7) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) 7) -- _deref_pbuc1=_dec__deref_pbuc1 + dec OAM_BUFFER+7 + // OAM_BUFFER[0x0b]--; + // [63] *((const nomodify byte*) OAM_BUFFER+(byte) $b) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) $b) -- _deref_pbuc1=_dec__deref_pbuc1 + dec OAM_BUFFER+$b + // OAM_BUFFER[0x0f]--; + // [64] *((const nomodify byte*) OAM_BUFFER+(byte) $f) ← -- *((const nomodify byte*) OAM_BUFFER+(byte) $f) -- _deref_pbuc1=_dec__deref_pbuc1 + dec OAM_BUFFER+$f + // moveLuigiLeft::@return + // } + // [65] return + rts +} + // moveLuigiRight +// move the Luigi sprites right +moveLuigiRight: { + // OAM_BUFFER[0x03]++; + // [66] *((const nomodify byte*) OAM_BUFFER+(byte) 3) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) 3) -- _deref_pbuc1=_inc__deref_pbuc1 + inc OAM_BUFFER+3 + // OAM_BUFFER[0x07]++; + // [67] *((const nomodify byte*) OAM_BUFFER+(byte) 7) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) 7) -- _deref_pbuc1=_inc__deref_pbuc1 + inc OAM_BUFFER+7 + // OAM_BUFFER[0x0b]++; + // [68] *((const nomodify byte*) OAM_BUFFER+(byte) $b) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) $b) -- _deref_pbuc1=_inc__deref_pbuc1 + inc OAM_BUFFER+$b + // OAM_BUFFER[0x0f]++; + // [69] *((const nomodify byte*) OAM_BUFFER+(byte) $f) ← ++ *((const nomodify byte*) OAM_BUFFER+(byte) $f) -- _deref_pbuc1=_inc__deref_pbuc1 + inc OAM_BUFFER+$f + // moveLuigiRight::@return + // } + // [70] return + rts +} + // File Data +.segment Data + PALETTE: .byte $f, $31, $32, $33, $f, $35, $36, $37, $f, $39, $3a, $3b, $f, $3d, $3e, $f, $f, $1c, $15, $14, $f, 2, $38, $3c, $f, $30, $37, $1a, $f, $f, $f, $f + // Small Luigi Sprite Data + SPRITES: .byte $80, $36, 2, $80, $80, $37, 2, $88, $88, $38, 2, $80, $88, $39, 2, $88 +.segment Tiles +TILES: +.import binary "smb1_chr.bin" + +.segment Vectors + VECTORS: .word vblank, main, 0 +.segment NesRom +NES_ROM: +.segmentout [ segments="Header" ] +.segmentout [ segments="ProgramRom" ] +.segmentout [ segments="CharacterRom" ] + + diff --git a/src/test/ref/examples/nes/nes-demo.sym b/src/test/ref/examples/nes/nes-demo.sym new file mode 100644 index 000000000..c257dd836 --- /dev/null +++ b/src/test/ref/examples/nes/nes-demo.sym @@ -0,0 +1,111 @@ +(label) @1 +(label) @begin +(label) @end +(const struct RICOH_2A03*) APU = (struct RICOH_2A03*) 16384 +(const nomodify byte*) FR_COUNTER = (byte*) 16407 +(const nomodify byte*) MEMORY = (byte*) 0 +(const byte*) NES_ROM[] = kickasm {{ .segmentout [ segments="Header" ] +.segmentout [ segments="ProgramRom" ] +.segmentout [ segments="CharacterRom" ] + }} +(const nomodify byte*) OAM_BUFFER = (byte*) 512 +(const byte) OFFSET_STRUCT_RICOH_2A03_DMC_FREQ = (byte) $10 +(const byte) OFFSET_STRUCT_RICOH_2A03_JOY1 = (byte) $16 +(const byte) OFFSET_STRUCT_RICOH_2A03_OAMDMA = (byte) $14 +(const byte) OFFSET_STRUCT_RICOH_2C02_OAMADDR = (byte) 3 +(const byte) OFFSET_STRUCT_RICOH_2C02_PPUADDR = (byte) 6 +(const byte) OFFSET_STRUCT_RICOH_2C02_PPUDATA = (byte) 7 +(const byte) OFFSET_STRUCT_RICOH_2C02_PPUMASK = (byte) 1 +(const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS = (byte) 2 +(const byte*) PALETTE[(number) $20] = { (byte) $f, (byte) $31, (byte) $32, (byte) $33, (byte) $f, (byte) $35, (byte) $36, (byte) $37, (byte) $f, (byte) $39, (byte) $3a, (byte) $3b, (byte) $f, (byte) $3d, (byte) $3e, (byte) $f, (byte) $f, (byte) $1c, (byte) $15, (byte) $14, (byte) $f, (byte) 2, (byte) $38, (byte) $3c, (byte) $f, (byte) $30, (byte) $37, (byte) $1a, (byte) $f, (byte) $f, (byte) $f, (byte) $f } +(const struct RICOH_2C02*) PPU = (struct RICOH_2C02*) 8192 +(const nomodify byte*) PPU_PALETTE = (byte*) 16128 +(const to_volatile byte*) PPU_PPUSTATUS = (byte*) 8194 +(byte) RICOH_2A03::DMC_FREQ +(byte) RICOH_2A03::DMC_LEN +(byte) RICOH_2A03::DMC_RAW +(byte) RICOH_2A03::DMC_START +(byte) RICOH_2A03::JOY1 +(byte) RICOH_2A03::JOY2 +(byte) RICOH_2A03::NOISE_HI +(byte) RICOH_2A03::NOISE_LO +(byte) RICOH_2A03::NOISE_VOL +(byte) RICOH_2A03::OAMDMA +(byte) RICOH_2A03::SND_CHN +(byte) RICOH_2A03::SQ1_HI +(byte) RICOH_2A03::SQ1_LO +(byte) RICOH_2A03::SQ1_SWEEP +(byte) RICOH_2A03::SQ1_VOL +(byte) RICOH_2A03::SQ2_HI +(byte) RICOH_2A03::SQ2_LO +(byte) RICOH_2A03::SQ2_SWEEP +(byte) RICOH_2A03::SQ2_VOL +(byte) RICOH_2A03::TRI_HI +(byte) RICOH_2A03::TRI_LINEAR +(byte) RICOH_2A03::TRI_LO +(byte) RICOH_2A03::UNUSED1 +(byte) RICOH_2A03::UNUSED2 +(byte) RICOH_2C02::OAMADDR +(byte) RICOH_2C02::OAMDATA +(byte) RICOH_2C02::PPUADDR +(byte) RICOH_2C02::PPUCTRL +(byte) RICOH_2C02::PPUDATA +(byte) RICOH_2C02::PPUMASK +(byte) RICOH_2C02::PPUSCROLL +(volatile byte) RICOH_2C02::PPUSTATUS loadstore +(const byte) SIZEOF_BYTE = (byte) 1 +(const byte*) SPRITES[] = { (byte) $80, (byte) $36, (byte) 2, (byte) $80, (byte) $80, (byte) $37, (byte) 2, (byte) $88, (byte) $88, (byte) $38, (byte) 2, (byte) $80, (byte) $88, (byte) $39, (byte) 2, (byte) $88 } +(const byte*) TILES[] = kickasm {{ .import binary "smb1_chr.bin" + }} +(const to_nomodify void()**) VECTORS[] = { &interrupt(HARDWARE_STACK)(void()) vblank(), &(void()) main(), (void()*) 0 } +(void()) initPaletteData() +(label) initPaletteData::@1 +(label) initPaletteData::@2 +(label) initPaletteData::@return +(byte) initPaletteData::i +(byte) initPaletteData::i#1 reg byte x 2002.0 +(byte) initPaletteData::i#2 reg byte x 1334.6666666666667 +(void()) initSpriteData() +(label) initSpriteData::@1 +(label) initSpriteData::@2 +(label) initSpriteData::@return +(byte) initSpriteData::i +(byte) initSpriteData::i#1 reg byte x 2002.0 +(byte) initSpriteData::i#2 reg byte x 1668.3333333333335 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::clearVBlankFlag1 +(label) main::disableAudioOutput1 +(label) main::disableVideoOutput1 +(label) main::enableVideoOutput1 +(byte) main::i +(byte) main::i#1 reg byte x 151.5 +(byte) main::i#2 reg byte x 112.22222222222223 +(label) main::waitForVBlank1 +(byte~) main::waitForVBlank1_$0 reg byte a 202.0 +(label) main::waitForVBlank1_@1 +(label) main::waitForVBlank2 +(byte~) main::waitForVBlank2_$0 reg byte a 202.0 +(label) main::waitForVBlank2_@1 +(void()) moveLuigiLeft() +(label) moveLuigiLeft::@return +(void()) moveLuigiRight() +(label) moveLuigiRight::@return +interrupt(HARDWARE_STACK)(void()) vblank() +(byte~) vblank::$1 reg byte a 4.0 +(byte~) vblank::$3 reg byte a 4.0 +(label) vblank::@1 +(label) vblank::@2 +(label) vblank::@3 +(label) vblank::@return + +reg byte x [ main::i#2 main::i#1 ] +reg byte x [ initSpriteData::i#2 initSpriteData::i#1 ] +reg byte x [ initPaletteData::i#2 initPaletteData::i#1 ] +reg byte a [ main::waitForVBlank1_$0 ] +reg byte a [ main::waitForVBlank2_$0 ] +reg byte a [ vblank::$1 ] +reg byte a [ vblank::$3 ] diff --git a/src/test/vs.code/kickc-test.code-workspace b/src/test/vs.code/kickc-test.code-workspace index 03e7aa8b7..79ce34f36 100644 --- a/src/test/vs.code/kickc-test.code-workspace +++ b/src/test/vs.code/kickc-test.code-workspace @@ -2,6 +2,9 @@ "folders": [ { "path": "../kc" + }, + { + "path": "../../../../tmp/first_nes" } ], "settings": {