diff --git a/data/opcodes.yaml b/data/opcodes.yaml index 43e14aa..ecbb676 100644 --- a/data/opcodes.yaml +++ b/data/opcodes.yaml @@ -280,7 +280,7 @@ :boundry_add: false :cpy: - :description: "ComPare X register" + :description: "ComPare Y register" :flags: - :s - :c diff --git a/lib/assembler.rb b/lib/assembler.rb index 3475179..bb58969 100644 --- a/lib/assembler.rb +++ b/lib/assembler.rb @@ -5,6 +5,9 @@ module Assembler6502 ## Let's simulate the entire 0xFFFF addressable memory space ## In the NES, and create reading and writing methods for it. class MemorySpace + INESHeaderSize = 0x10 + ProgROMSize = 0x4000 + CharROMSize = 0x2000 #### ## Create a completely zeroed memory space @@ -84,7 +87,6 @@ module Assembler6502 fail(SyntaxError, "Already got ines header") unless @ines_header.nil? @ines_header = parsed_line puts "\tGot iNES Header" - #memory.write(0x0, parsed_line.emit_bytes) when Org address = parsed_line.address @@ -99,22 +101,22 @@ module Assembler6502 puts "\tSaving instruction with unresolved symbols #{parsed_line}, for second pass" unresolved_instructions << parsed_line else - puts "\tWriting instruction #{parsed_line} to memory" + puts "\tWriting instruction #{parsed_line}" memory.write(parsed_line.address, parsed_line.emit_bytes) end address += parsed_line.length - puts "\tAdvanced address to %X" % address when IncBin - fail("\tI Don't support .incbin yet") - + puts "\t Including binary file #{parsed_line.filepath}" + memory.write(parsed_line.address, parsed_line.emit_bytes) + address += parsed_line.size when DW if parsed_line.unresolved_symbols? - puts "\tSaving .dw directive with unresolved symbols #{parsed_line}, for second pass" + puts "\tSaving #{parsed_line} directive with unresolved symbols, for second pass" unresolved_instructions << parsed_line else - puts "\tWriting .dw #{parsed_line.inspect} to memory" + puts "\tWriting #{parsed_line} to memory" memory.write(address, parsed_line.emit_bytes) end address += 2 @@ -136,11 +138,12 @@ module Assembler6502 end end - print "Second pass: Resolving Symbols..." + puts "Second pass: Resolving Symbols..." unresolved_instructions.each do |instruction| if instruction.unresolved_symbols? instruction.resolve_symbols(labels) end + puts "\tResolved #{instruction}" memory.write(instruction.address, instruction.emit_bytes) end puts 'Done' @@ -155,7 +158,7 @@ module Assembler6502 ## I am guessing the ROM size should be 1 bank of 16KB cartridge ROM ## plus the 16 byte iNES header. If the ROM is written into memory ## beginning at 0xC000, this should reach right up to the interrupt vectors - def assemble + def assemble_old virtual_memory = assemble_in_virtual_memory ## First we need to be sure we have an iNES header @@ -190,6 +193,58 @@ module Assembler6502 #nes_rom.emit_bytes end + + #### + ## Another try at using the header to decide which segments + ## go into the final ROM image, and which order. + def assemble + virtual_memory = assemble_in_virtual_memory + + ## First we need to be sure we have an iNES header + fail(INESHeaderNotFound) if @ines_header.nil? + + ## Now, we should decide how big the ROM image will be. + ## And reserve memory build the image in + nes_rom_size = MemorySpace::INESHeaderSize + nes_rom_size += @ines_header.prog * MemorySpace::ProgROMSize + nes_rom_size += @ines_header.char * MemorySpace::CharROMSize + nes_rom = MemorySpace.new(nes_rom_size) + puts "ROM will be #{nes_rom_size} bytes" + + ## Write the ines header to the ROM + nes_rom.write(0x0, @ines_header.emit_bytes) + puts "Wrote 16 byte ines header" + + ## If prog rom is >= 1 write the 16kb chunk from 0x8000 + if @ines_header.prog >= 1 + nes_rom.write(0x10, virtual_memory.read(0x8000, MemorySpace::ProgROMSize)) + puts "Wrote 16KB byte prog rom 1" + end + + ## If prog rom is == 2 write the 16kb chunk from 0xC000 + if @ines_header.prog == 2 + nes_rom.write(0x10 + 0x4000, virtual_memory.read(0xC000, MemorySpace::ProgROMSize)) + puts "Wrote 16KB byte prog rom 2" + end + fail("Can only have 2 prog rom slots") if @ines_header.prog > 2 + + ## If char rom is >= 1 write the 8kb chunk from 0x0000 + if @ines_header.char >= 1 + char_start = 0x10 + (@ines_header.prog * MemorySpace::ProgROMSize) + nes_rom.write(char_start, virtual_memory.read(0x0000, MemorySpace::CharROMSize)) + puts "Wrote 8KB byte char rom 1" + end + + ## If char rom is == 2 write the 8kb chunk from 0x2000 + if @ines_header.char >= 1 + char_start = 0x10 + (@ines_header.prog * MemorySpace::ProgROMSize) + MemorySpace::CharROMSize + nes_rom.write(char_start, virtual_memory.read(0x2000, MemorySpace::CharROMSize)) + puts "Wrote 8KB byte char rom 2" + end + + nes_rom.emit_bytes + end + end end diff --git a/lib/directive.rb b/lib/directive.rb index 94c15e9..822d8e1 100644 --- a/lib/directive.rb +++ b/lib/directive.rb @@ -49,12 +49,15 @@ module Assembler6502 #### ## This is to include a binary file class IncBin + attr_reader :address, :filepath class FileNotFound < StandardError; end #### ## Initialize with a file path - def initialize(filepath) + def initialize(filepath, address) + @filepath = filepath + @address = address unless File.exists?(filepath) fail(FileNotFound, ".incbin can't find #{filepath}") end @@ -97,6 +100,14 @@ module Assembler6502 end end + def to_s + if @value.kind_of?(Symbol) + sprintf("$%.4X | .dw #{@value}", @address) + else + sprintf("$%.4X | .dw $%.4X", @address, @value) + end + end + def emit_bytes fail('Need to resolve symbol in .dw directive') if unresolved_symbols? [@value & 0xFFFF].pack('S').bytes @@ -155,7 +166,7 @@ module Assembler6502 Org.new($1.to_i(16)) when /^\.incbin "([^"]+)"$/ - IncBin.new($1) + IncBin.new($1, address) when /^\.dw\s+\$([0-9A-F]{1,4})$/ DW.new($1.to_i(16), address) diff --git a/nsf_player.asm b/nsf_player.asm new file mode 100644 index 0000000..92b0ae1 --- /dev/null +++ b/nsf_player.asm @@ -0,0 +1,82 @@ +; Create an iNES header +.ines {"prog": 2, "char": 0, "mapper": 0, "mirror": 1} + +; The supermario.nsf file is 17084 bytes, this includes a 0x80 byte header. +; It is supposed to be loaded into memory 0x8000, but we don't want that +; header, so let's include the nsf to 0x8000 - 0x80 so it lines up properly + +.org $7F80 +.incbin "super_mario.nsf" + +; Had to start this prog segment a bit later because the mario nsf is > 16KB +.org $C300 +start: + CLD + SEI + LDA #$00 + STA $2000 + +; Wait for 2 vblanks +wait_vb1: + LDA $2002 + BPL wait_vb1 + +wait_vb2: + LDA $2002 + BPL wait_vb2 + +; Clear out the sound registers + LDA #$00 + LDX #$00 +clear_sound_registers: + STA $4000, X + INX + CPX #$0F + BNE clear_sound_registers + + LDA #$10 + STA $4010 + LDA #$00 + STA $4011 + STA $4012 + STA $4013 + +; Enable sound channels (except DMC) + LDA #$0F + STA $4015 + +; Reset frame counter and clock divider + LDA #$C0 + STA $4017 + +; Set song and NTSC + LDA #$00 ; Song 0 + LDX #$00 ; NTSC + JSR $8000 + +; Enable Vblank NMI + LDA #$80 + STA $2000 + +forever: + JMP forever + +nmi: + LDA $2002 + LDA #$00 + STA $2000 + LDA $80 + STA $2000 + JSR $8000 + RTI + +irq: + RTI + +.ascii "The end of prog2" + +.org $FFFA + .dw nmi + .dw start + .dw irq + diff --git a/some_beeps.nsf b/some_beeps.nsf new file mode 100644 index 0000000..eb1ada8 Binary files /dev/null and b/some_beeps.nsf differ diff --git a/whatever.inc b/whatever.inc new file mode 100644 index 0000000..856242f --- /dev/null +++ b/whatever.inc @@ -0,0 +1,2 @@ + + ޭޭޭޭޭޭޭޭ \ No newline at end of file