1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-10-21 17:24:39 +00:00

Added support for NES platform with a working demo program. Closes #456

This commit is contained in:
jespergravgaard 2020-05-24 00:28:38 +02:00
parent 0ed5059c8a
commit c8cd5a0e51
15 changed files with 3632 additions and 0 deletions

View File

@ -0,0 +1,3 @@
eor #$ff
sec
adc ({z1}),y

29
src/main/kc/include/nes.h Normal file
View File

@ -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 <ricoh_2c02.h>
#include <ricoh_2a03.h>
// 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();

View File

@ -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;

View File

@ -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;

38
src/main/kc/lib/nes.c Normal file
View File

@ -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 <nes.h>
// 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 }
}

27
src/main/kc/target/nes.ld Normal file
View File

@ -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

View File

@ -0,0 +1,10 @@
{
"extension": "nes",
"link": "nes.ld",
"cpu": "MOS6502",
"emulator": "nestopia",
"zp_reserve": [ ],
"defines": {
"__NES__": 1
}
}

View File

@ -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");

View File

@ -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 <nes.h>
#include <string.h>
// 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 = <PPU_PALETTE;
// Write to PPU
for(char i=0;i<sizeof(PALETTE);i++)
PPU->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;i<sizeof(SPRITES);i++)
OAM_BUFFER[i] = SPRITES[i];
}
char PALETTE[0x20] = {
// Background palettes
0x0f, 0x31, 0x32, 0x33,
0x0f, 0x35, 0x36, 0x37,
0x0f, 0x39, 0x3a, 0x3b,
0x0f, 0x3d, 0x3e, 0x0f,
// Sprite palettes (selected by the attribute bits 0-1 of the sprites)
0x0f, 0x1c, 0x15, 0x14,
0x0f, 0x02, 0x38, 0x3c,
0x0f, 0x30, 0x37, 0x1a, // Luigi-like colors
0x0f, 0x0f, 0x0f, 0x0f // All black
};
// Small Luigi Sprite Data
char SPRITES[] = {
// Y , TILE, ATTR , X
128, 0x36, 0b00000010, 128, // Sprite 0
128, 0x37, 0b00000010, 136, // Sprite 1
136, 0x38, 0b00000010, 128, // Sprite 2
136, 0x39, 0b00000010, 136 // Sprite 3
};
// Tiles
#pragma data_seg(Tiles)
export char TILES[] = kickasm(resource "smb1_chr.bin") {{
.import binary "smb1_chr.bin"
}};
// Interrupt Vectors
#pragma data_seg(Vectors)
export void()* const VECTORS[] = {
// NMI Called when the PPU refreshes the screen (also known as the V-Blank period)
&vblank,
// RESET Called when the NES is reset, including when it is turned on.
&main,
// IRQ Called when a BRK instruction is executed.
0
};
// Generate the NES ROM contents
// Can be moved into the linker file when KickAsm 5.15 is released.
#pragma data_seg(NesRom)
export char NES_ROM[] = kickasm {{
.segmentout [ segments="Header" ]
.segmentout [ segments="ProgramRom" ]
.segmentout [ segments="CharacterRom" ]
}};

Binary file not shown.

View File

@ -0,0 +1,273 @@
// A minimal NES demo
// Based on: https://github.com/gregkrsak/first_nes written by Greg M. Krsak, 2018.
// 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
.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
.segment Code
// RESET Called when the NES is reset, including when it is turned on.
main: {
// asm
cld
ldx #$ff
txs
// PPU->PPUCTRL = 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;i<sizeof(SPRITES);i++)
cpx #$10*SIZEOF_BYTE
bcc __b2
// }
rts
__b2:
// OAM_BUFFER[i] = SPRITES[i]
lda SPRITES,x
sta OAM_BUFFER,x
// for(char i=0;i<sizeof(SPRITES);i++)
inx
jmp __b1
}
// Copy palette values to PPU
initPaletteData: {
// asm
// Reset the high/low latch to "high"
lda PPU_PPUSTATUS
// PPU->PPUADDR = >PPU_PALETTE
// Write the high byte of PPU Palette address
lda #>PPU_PALETTE
sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUADDR
// PPU->PPUADDR = <PPU_PALETTE
// Write the low byte of PPU Palette address
lda #0
sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUADDR
tax
// Write to PPU
__b1:
// for(char i=0;i<sizeof(PALETTE);i++)
cpx #$20*SIZEOF_BYTE
bcc __b2
// }
rts
__b2:
// PPU->PPUDATA = PALETTE[i]
lda PALETTE,x
sta PPU+OFFSET_STRUCT_RICOH_2C02_PPUDATA
// for(char i=0;i<sizeof(PALETTE);i++)
inx
jmp __b1
}
// NMI Called when the PPU refreshes the screen (also known as the V-Blank period)
vblank: {
pha
txa
pha
tya
pha
// PPU->OAMADDR = 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" ]

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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 ]

View File

@ -2,6 +2,9 @@
"folders": [
{
"path": "../kc"
},
{
"path": "../../../../tmp/first_nes"
}
],
"settings": {