From 404934479fb1fe897a61b5d6d3571a5ef95ded6e Mon Sep 17 00:00:00 2001 From: Safiire Date: Sun, 22 Feb 2015 05:59:03 -0800 Subject: [PATCH 1/3] Intermediate commit --- data/opcodes.yaml | 2 +- lib/assembler.rb | 73 ++++++++++++++++++++++++++++++++++++----- lib/directive.rb | 15 +++++++-- nsf_player.asm | 82 ++++++++++++++++++++++++++++++++++++++++++++++ some_beeps.nsf | Bin 0 -> 5931 bytes whatever.inc | 2 ++ 6 files changed, 162 insertions(+), 12 deletions(-) create mode 100644 nsf_player.asm create mode 100644 some_beeps.nsf create mode 100644 whatever.inc 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 0000000000000000000000000000000000000000..eb1ada8721c2efbdf9cc374bff2a31cfded068a8 GIT binary patch literal 5931 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 M{owEVw%ud?A6irrUjP6A literal 0 HcmV?d00001 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 From a52ead091ed4d044d980115765d58cead11a5a1a Mon Sep 17 00:00:00 2001 From: Safiire Date: Sun, 22 Feb 2015 06:33:36 -0800 Subject: [PATCH 2/3] this branch sucks --- lib/assembler.rb | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/lib/assembler.rb b/lib/assembler.rb index bb58969..2259857 100644 --- a/lib/assembler.rb +++ b/lib/assembler.rb @@ -158,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_old + def assemble virtual_memory = assemble_in_virtual_memory ## First we need to be sure we have an iNES header @@ -197,7 +197,8 @@ module Assembler6502 #### ## Another try at using the header to decide which segments ## go into the final ROM image, and which order. - def assemble + ## This does not work, it needs to know about banks. + def assemble_new_crap virtual_memory = assemble_in_virtual_memory ## First we need to be sure we have an iNES header @@ -222,25 +223,25 @@ module Assembler6502 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 @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 @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 + #if @ines_header.char == 2 + #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 From d10709e0b02bc56b58a9ee0b1cf960cb0ca4c748 Mon Sep 17 00:00:00 2001 From: Safiire Date: Sun, 22 Feb 2015 17:39:35 -0800 Subject: [PATCH 3/3] Some updates but assemblying programs with multiple banks, or more prog and char sections is messed up --- lib/assembler.rb | 56 +++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/lib/assembler.rb b/lib/assembler.rb index 2259857..a407877 100644 --- a/lib/assembler.rb +++ b/lib/assembler.rb @@ -195,38 +195,36 @@ module Assembler6502 #### - ## Another try at using the header to decide which segments - ## go into the final ROM image, and which order. - ## This does not work, it needs to know about banks. - def assemble_new_crap - virtual_memory = assemble_in_virtual_memory + ## This is all crap, I must research how banks and mappers work + #def assemble_new_crap + # virtual_memory = assemble_in_virtual_memory - ## First we need to be sure we have an iNES header - fail(INESHeaderNotFound) if @ines_header.nil? + # ## 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" + # ## 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" + # ## 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 >= 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 + # ## 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 @@ -243,8 +241,8 @@ module Assembler6502 #puts "Wrote 8KB byte char rom 2" #end - nes_rom.emit_bytes - end + #nes_rom.emit_bytes + #end end