diff --git a/examples/music_driver.asm b/examples/music_driver.asm deleted file mode 100644 index 358ad4e..0000000 --- a/examples/music_driver.asm +++ /dev/null @@ -1,202 +0,0 @@ -;------------------------------------------------------------------------------ -; An NES music engine that understands the binary stream outputted from my -; MIDI converter :) -;;;; -; Create an iNES header -.ines {"prog": 1, "char": 0, "mapper": 0, "mirror": 0} - - -;;;; -; Include all the symbols in the nes library -.inc - - -;;;; -; Let's put a data structure to control the sound engine in the zero page -.org $0000 -.scope sound_engine - ; Where we are reading from ROM - .space stream_read_ptr_lo 1 - .space stream_read_ptr_hi 1 - - ; Where we are writing in the APU - .space stream_write_ptr_lo 1 - .space stream_write_ptr_hi 1 - .space delta 1 -. - - -;;;; -; Open the prog section bank 0 -.segment prog 0 - - -;;;; -; Setup the interrupt vectors -.org $FFFA -.dw vblank -.dw main -.dw irq - - -;;;; -; Here is our code entry point, which we'll call main. -.org $C000 -.scope main - ; Disable interrupts and decimal flag - sei - cld - - ; Wait for 2 vblanks - wait_vb1: - lda nes.ppu.status - bpl wait_vb1 - wait_vb2: - lda nes.ppu.status - bpl wait_vb2 - - ; Now we want to initialize the hardware to a known state - lda #%00 - ldx #$00 - clear_segments: - sta $0, x - sta $100, x - sta $200, x - sta $300, x - sta $400, x - sta $500, x - sta $600, x - sta $700, x - inx - bne clear_segments - - ; Reset the stack pointer - ldx #$FF - txs - - ; Disable all graphics and vblank nmi - lda #$00 - sta nes.ppu.control - sta nes.ppu.mask - - jsr init_sound - - ; Resume interrupts and NMI and loop here forever - lda #%10000000 - sta nes.ppu.control - cli - forever: - jmp forever -. - - -;;;; -; Initialize the APU to enable Pulse1 -.scope init_sound - lda #$00 - ldy #$00 - clear_apu: - sta nes.apu, y - iny - cpy #$10 - bne clear_apu - - lda #>music_buffer - ldx # 'AAA $FF, Y', :display => '%s $%.2X, Y', :regex => /^#{Mnemonic}\s+#{Num8}\s?,\s?#{YReg}$/, - :regex_label => /^#{Mnemonic}\s+#{Sym}\s?,\s?#{YReg} zp$/ + :regex_label => /^#{Mnemonic}\s+#{Sym}\s?,\s?#{YReg}\s+zp$/ }, :absolute => { @@ -220,7 +220,7 @@ module N65 end case @arg - when Fixnum, NilClass + when Integer, NilClass assembler.write_memory(emit_bytes) when String begin diff --git a/lib/n65/version.rb b/lib/n65/version.rb index 9ec4392..02c9b57 100644 --- a/lib/n65/version.rb +++ b/lib/n65/version.rb @@ -1,3 +1,3 @@ module N65 - VERSION = "0.5.0" + VERSION ||= "1.0.0" end diff --git a/utils/midi/Makefile b/utils/midi/Makefile deleted file mode 100644 index e30bd7b..0000000 --- a/utils/midi/Makefile +++ /dev/null @@ -1,3 +0,0 @@ - -build: convert - clang++ source/*.cpp -I include -o convert diff --git a/utils/midi/c_scale.mid b/utils/midi/c_scale.mid deleted file mode 100644 index 4adca17..0000000 Binary files a/utils/midi/c_scale.mid and /dev/null differ diff --git a/utils/midi/convert b/utils/midi/convert deleted file mode 100755 index 33feb1b..0000000 Binary files a/utils/midi/convert and /dev/null differ diff --git a/utils/midi/guitar.mid b/utils/midi/guitar.mid deleted file mode 100644 index e6e0ebe..0000000 Binary files a/utils/midi/guitar.mid and /dev/null differ diff --git a/utils/midi/include/event.h b/utils/midi/include/event.h deleted file mode 100644 index 9474735..0000000 --- a/utils/midi/include/event.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef MIDI_EVENT_H -#define MIDI_EVENT_H - -#include -#include -#include "helpers.h" - -namespace Midi { - -enum MidiMessageType {Midi = 0, MidiNoteOff = 0x8, MidiNoteOn, MidiPolyAftertouch, - MidiControlChange, MidiProgramChange, MidiChannelAftertouch, - MidiPitchWheel, MidiMetaEvent = 0xFF, MidiSysex1 = 0xF0, MidiSysex2 = 0xF7}; - - -enum MidiMetaType {MetaSequenceNumber = 0x0, MetaTextEvent = 0x1, MetaCopyright = 0x2, - MetaTrackName = 0x3, MetaInstrumentName = 0x4, MetaLyricText = 0x5, - MetaMarkerText = 0x6, MetaCuePoint = 0x7, MetaChannelPrefixAssignment = 0x20, - MetaEndOfTrack = 0x2F, MetaTempoSetting = 0x51, MetaSMPTEOffset = 0x54, - MetaTimeSignature = 0x58, MetaKeySignature = 0x59, MetaSequenceSpecific = 0x7F}; - -/*** - * Spec: - * MidiTrackEvent = + | | - * MidiEvent = + + - * MetaEvent = 0xFF + + + - * SysexEvent = 0xF0 + + 0xF7 -or- - * SysexEvent = 0xF7 + 0xF7 - ***/ - -class Event { - private: - unsigned int m_delta; - unsigned int m_size; - unsigned int m_data_size; - unsigned char m_status; - unsigned char m_meta_type; - unsigned char m_parameter1; - unsigned char m_parameter2; - void *m_data; - - public: - Event(); - Event(int delta, int status, int parameter1, int parameter2); - void init_midi(int delta, int status, int parameter1, int parameter2); - void init_from_file(FILE *fp, unsigned char last_status); - ~Event(); - - // Some easy inline functions - static bool is_status_byte(unsigned char byte) { return ((byte & 0x80) >> 7) == 1; } - unsigned int delta() const { return m_delta; } - unsigned char status() const { return m_status; } - unsigned int bytes_read() const { return m_size; } - unsigned char parameter1() const { return m_parameter1; } - unsigned char parameter2() const { return m_parameter2; } - int message_type() const { return (m_status &0xF0) >> 4; } - int channel() const { return (m_status &0xF); } - void *sysex_data() const { return m_data; } - - - void print_yaml(){ - printf(" - :delta: 0x%X\n", m_delta); - printf(" :status: 0x%X\n", m_status); - - switch(m_status){ - case 0xFF: - printf(" :meta_type: 0x%X\n", m_meta_type); - printf(" :meta_data_size: 0x%X\n", m_data_size); - break; - case 0xF0: - case 0xF7: - printf(" :sysex_data_size: %d\n", m_data_size); - break; - default: - printf(" :parameter1: 0x%X\n", m_parameter1); - printf(" :parameter2: 0x%X\n", m_parameter2); - } - /* Printing out the actual data as a string confuses the YAML parser - * So let's just not do that. - if(m_data){ - printf(" :data: \""); - - for(int i = 0; i < m_data_size; i++){ - printf("%c", ((unsigned char*)m_data)[i]); - } - printf("\"\n"); - } - */ - } -}; - -} - -#endif diff --git a/utils/midi/include/file.h b/utils/midi/include/file.h deleted file mode 100644 index 7be5a33..0000000 --- a/utils/midi/include/file.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef MIDI_FILE_H -#define MIDI_FILE_H - -#include -#include "track.h" - -namespace Midi { - -/*** - * Spec: - * MidiFile = + [+ ...] - * - * midi_header_t = "MThd" + + + + - * - ***/ - -typedef struct midi_header_t { - char cookie[4]; - unsigned int size; - unsigned short format; - unsigned short track_count; - unsigned short ticks_per_quarter_note; -} __attribute__((packed)) midi_header_t; - - -class File { - private: - midi_header_t m_header; - - public: - File(void); - ~File(void); - std::vector m_tracks; - void init_from_file(const char *filename); - - void print_yaml(){ - printf("---\n"); - printf(":midi_file:\n"); - printf(" :header: Mthd\n"); - printf(" :size: %u\n", m_header.size); - printf(" :format: %u\n", m_header.format); - printf(" :track_count: %u\n", m_header.track_count); - printf(" :ticks_per_quarter_note: %u\n", m_header.ticks_per_quarter_note); - printf(" :tracks:\n"); - - for(int i = 0; i < m_tracks.size(); i++){ - if(i == 0){ - m_tracks[i]->print_yaml(); - }else{ - m_tracks[i]->print_yaml(); - } - } - } -}; - -} -#endif diff --git a/utils/midi/include/helpers.h b/utils/midi/include/helpers.h deleted file mode 100644 index 7c5da2b..0000000 --- a/utils/midi/include/helpers.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef MIDI_HELPERS_H -#define MIDI_HELPERS_H - -#include -#include - -namespace Midi { - -unsigned int read_variable_length(FILE *fp, unsigned int *value_size); -short swap_endian_16(short big_endian); -int swap_endian_32(int big_endian); - -} -#endif diff --git a/utils/midi/include/track.h b/utils/midi/include/track.h deleted file mode 100644 index 210ba49..0000000 --- a/utils/midi/include/track.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef MIDI_TRACK_H -#define MIDI_TRACK_H - -#include "event.h" -#include - -namespace Midi { - -/*** - * Spec: - * MidiTrack = + [+ ...] - * midi_track_header_t = "MTrk" + - * - ***/ - -typedef struct midi_track_header_t { - char cookie[4]; - unsigned int size; -} __attribute__((packed)) midi_track_header_t; - - -class Track { - private: - unsigned int m_total_size; - midi_track_header_t m_header; - - public: - Track(void); - ~Track(void); - void init_from_file(FILE *fp); - std::vector m_events; - - void print_yaml(){ - printf(" - :header: MTrk\n"); - printf(" :total_size: %u\n", m_header.size); - printf(" :events:\n"); - - for(int i = 0; i < m_events.size(); i++){ - m_events[i]->print_yaml(); - } - } -}; - -} -#endif diff --git a/utils/midi/lil_melody.mid b/utils/midi/lil_melody.mid deleted file mode 100644 index 5334f29..0000000 Binary files a/utils/midi/lil_melody.mid and /dev/null differ diff --git a/utils/midi/mi_feabhra.mid b/utils/midi/mi_feabhra.mid deleted file mode 100644 index 4875ea7..0000000 Binary files a/utils/midi/mi_feabhra.mid and /dev/null differ diff --git a/utils/midi/midi_to_nes.rb b/utils/midi/midi_to_nes.rb deleted file mode 100755 index bce4aa3..0000000 --- a/utils/midi/midi_to_nes.rb +++ /dev/null @@ -1,204 +0,0 @@ -#!/usr/bin/env ruby - -require 'yaml' - -class MidiToNES - - #### Custom Exceptions - class MidiFormatNotSupported < StandardError; end - - #### Some Constants - NoteOff = 0x8 - NoteOn = 0x9 - - #### A440 Tuning, and NES CPU speed in hz - Tuning = 440.0 - CPU = 1789773.0 - - - #### LSB Address registers of the APU, MSB is always 0x40 - Pulse1Control = 0x00 - Pulse1FT = 0x2 - Pulse1CT = 0x3 - - - #### - ## Initialize from a yaml file - def self.init_from_file(filename, bpm) - self.new(File.read(filename), bpm) - end - - - #### - ## Initialize with a yaml string - def initialize(yaml_string, bpm) - @bpm = bpm.to_f - @midi_data = YAML.load(yaml_string)[:midi_file] - unless @midi_data[:format].zero? - fail(MidiFormatNotSupported, "Currently only supports format 0 Midi Files") - end - @ticks_per_quarter_note = @midi_data[:ticks_per_quarter_note] - end - - - #### - ## Write to binary file - def write_binary(filename) - binary = convert - File.open(filename, 'wb') do |fp| - fp.write(binary) - end - end - - - #### - ## For now assume one track - def convert - tick_count = 1 - events = [] - - track = @midi_data[:tracks].first - track[:events].each do |event| - - delta, status, note, velocity = event.values_at(:delta, :status, :parameter1, :parameter2) - - ## The status byte contains both the Midi message type, and channel. - type = (status & 0b11110000) >> 4 - channel = status & 0b00001111 - - ## We only care about note on and off, and only care about channel 0 for now. - next unless type == NoteOn || type == NoteOff - next unless channel.zero? - - ## Update the total time - tick_count += delta - - ## Ok this is a note either turning on or off - if type == NoteOff || velocity.zero? - #event = {:start => tick_count, :note => note, :velocity => 0} - #events << event - else - event = {:start => tick_count, :note => note, :velocity => velocity} - events << event - end - end - - ## Finally sort event list by start time - events.sort! do |a, b| - a[:start] <=> b[:start] - end - - ## Now convert these events to a bytestream for our NES sound engine - events_to_byte_stream(events) - end - - - #### - ## This converts a list of note events into a byte stream for updating NES APU registers - def events_to_byte_stream(events) - last_tick = 1 - byte_stream = [] - - events.each do |event| - ## Work out the delta again - delta = event[:start] - last_tick - byte_stream << midi_tick_to_vblank(delta) # Delta - byte_stream << pulse_control_value(event) # Value - if event[:velocity].zero? - #byte_stream << 0 # Off with 0 frequency timer - #byte_stream << 0 - else - byte_stream << pulse_ft_value(event) # Value - byte_stream << pulse_ct_value(event) # Value - end - last_tick += delta - end - byte_stream.pack('C*') - end - - - #### - ## Given an event, produce a value for register nes.apu.pulse1.control - ## DDLC VVVV - ## Duty (D), envelope loop / length counter halt (L), constant volume (C), volume/envelope (V) - def pulse_control_value(event) - ## Start with 50% duty cycle, length counter halt is on - ## Constant volume is On, and volume is determined by bit-reducing the event velocity to 4-bit - value = 0b10000111 - - #four_bit_max = (2**4 - 1) - #seven_bit_max = (2**7 - 1) - - #volume_float = event[:velocity] / seven_bit_max.to_f - #volume_4_bit = (volume_float * four_bit_max).round & 0b00001111 - - #value | volume_4_bit - end - - - #### - ## Given an event, produce a value for register nes.apu.pulse1.ft - ## TTTT TTTT - ## This is the low byte of the timer, the higher few bits being in pulse1.ct - def pulse_ft_value(event) - midi_note_to_nes_timer(event[:note]) & 0xff - end - - - #### - ## Given an event, produce a value for register nes.apu.pulse1.ct - ## LLLL LTTT - ## This has the higher 3 bits of the timer, and L is the length counter. - ## For now let's just use duration as the length counter. - def pulse_ct_value(event) - value = 0b11111000 - - ## We will grab the high 3 bits of the 11-bit timer value now - timer_high_3bit = midi_note_to_nes_timer(event[:note]) & 0b11100000000 - value | (timer_high_3bit >> 8) - end - - - #### - ## Midi note to NES timer - def midi_note_to_nes_timer(midi_note) - frequency = Tuning * 2**((midi_note - 69) / 12.0) - timer = (CPU / (16 * frequency)) - 1 - if timer > (2**11 - 1) - fail("midi note #{midi_note} is too big at #{timer}") - end - timer.round - end - - - #### - ## Convert a MIDI tick delta to an NES vblank delta. - def midi_tick_to_vblank(midi_tick) - quarter_note_in_seconds = 60 / @bpm - vblanks_per_quarter_note = quarter_note_in_seconds / (1/60.0) - tick_normalized = midi_tick / @ticks_per_quarter_note.to_f - vblanks = tick_normalized * vblanks_per_quarter_note - vblanks.round - end - -end - -if __FILE__ == $0 - unless ARGV.size == 2 - STDERR.puts("Usage #{$0} ") - exit(1) - end - - bpm, midi_file = ARGV - - ## Run the midi file through my converter written in C++ - IO.popen("./convert #{midi_file}") do |io| - midi_to_nes = MidiToNES.new(io.read, bpm.to_i) - midi_to_nes.write_binary('../../data.mus') - end - -end - - - - diff --git a/utils/midi/source/convert.cpp b/utils/midi/source/convert.cpp deleted file mode 100644 index 92a4ef8..0000000 --- a/utils/midi/source/convert.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include "file.h" - -int main(int argc, char **argv){ - - if(argc < 1){ - printf("Need a midi file argument\n"); - exit(1); - } - - Midi::File midi_file; - midi_file.init_from_file(argv[1]); - midi_file.print_yaml(); - - return 0; -} diff --git a/utils/midi/source/event.cpp b/utils/midi/source/event.cpp deleted file mode 100644 index bd575a5..0000000 --- a/utils/midi/source/event.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "event.h" - -namespace Midi { - -//// -// Default constructor -Event::Event(void) : - m_delta(0), m_size(0), - m_data_size(0), m_status(0), - m_meta_type(0), m_parameter1(0), - m_parameter2(0), m_data(NULL) { } - - -//// -// Constructor to set specific fields -Event::Event(int delta, int status, int parameter1, int parameter2) : - m_delta(delta), m_size(4), - m_data_size(0), m_status(status), - m_meta_type(0), m_parameter1(parameter1), - m_parameter2(parameter2), m_data(NULL) { } - - -//// -// Use a file pointer to initialize -void Event::init_from_file(FILE *fp, unsigned char last_status){ - fpos_t saved_position; - unsigned int value_size; - // All types of events are preceeded by a uintvar delta time - m_delta = read_variable_length(fp, &value_size); - m_size += value_size; - - // All types then have a status byte, unless they are reusing - // the previous status, get ready to rewind if this is the case. - fgetpos(fp, &saved_position); - fread(&m_status, sizeof(unsigned char), 1, fp); - m_size++; - - if(!is_status_byte(m_status)){ - // This is not a status byte, so it must be reusing the previous one, Rewind - m_status = last_status; - fsetpos(fp, &saved_position); - m_size--; - } - - switch(m_status){ - case 0xFF: // Meta - // This will have a meta sub type - fread(&m_meta_type, sizeof(unsigned char), 1, fp); - m_size++; - // Now a variable size for data - m_data_size = read_variable_length(fp, &value_size); - m_size += value_size; - // Finally, read the meta data - m_data = malloc(sizeof(unsigned char) * m_data_size); - fread(m_data, sizeof(unsigned char), m_data_size, fp); - m_size += m_data_size; - break; - case 0xF0: // Sysex - case 0xF7: // Sysex - // Sysex data runs until the next 0xF0 or 0xF7, count how many byte to allocate, and rewind - fgetpos(fp, &saved_position); - while(m_status != fgetc(fp)){ - m_data_size++; - } - fsetpos(fp, &saved_position); - - m_data = malloc(sizeof(unsigned char) * m_data_size); - fread(m_data, sizeof(unsigned char), m_data_size, fp); - m_size += (m_data_size + 1); - fgetc(fp); // Throw away the end byte, although it counts for size - break; - default: // Midi - fread(&m_parameter1, sizeof(unsigned char), 1, fp); - fread(&m_parameter2, sizeof(unsigned char), 1, fp); - m_size += 2; - break; - } -} - - -void Event::init_midi(int delta, int status, int parameter1, int parameter2){ - m_delta = delta; - m_status = status; - m_parameter1 = parameter1; - m_parameter2 = parameter2; - m_size = 4; -} - - -Event::~Event(void){ - if(m_data){ - free(m_data); - } -} - -} diff --git a/utils/midi/source/file.cpp b/utils/midi/source/file.cpp deleted file mode 100644 index 2092173..0000000 --- a/utils/midi/source/file.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "file.h" - -namespace Midi { - -File::File(void){ - m_header.cookie[0] = m_header.cookie[1] = m_header.cookie[2] = m_header.cookie[3] = 0; - m_header.size = 0; -} - - -void File::init_from_file(const char *filename){ - FILE *fp = fopen(filename, "rb"); - fread(&m_header, sizeof(midi_header_t), 1, fp); - // Fix up this Big Endian stuff - m_header.size = swap_endian_32(m_header.size); - m_header.format = swap_endian_16(m_header.format); - m_header.track_count = swap_endian_16(m_header.track_count); - m_header.ticks_per_quarter_note = swap_endian_16(m_header.ticks_per_quarter_note); - - // Read each track - Track *track; - for(int i = 0; i < m_header.track_count; i++){ - track = new Track(); - track->init_from_file(fp); - m_tracks.push_back(track); - } -} - - -File::~File(void){ - // Free all the midi tracks - for(unsigned int i = 0; i < m_tracks.size(); i++){ - delete m_tracks[i]; - } -} - -} diff --git a/utils/midi/source/helpers.cpp b/utils/midi/source/helpers.cpp deleted file mode 100644 index 8eee332..0000000 --- a/utils/midi/source/helpers.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "helpers.h" - - -namespace Midi { - -//// -// I have eliminated any doubt that this function works on variable -// length uintvars up to 4 bytes. -unsigned int read_variable_length(FILE *fp, unsigned int *value_size) { - unsigned int value; - *value_size = 1; - - if((value = fgetc(fp)) & 0x80){ - unsigned char c; - value &= 0x7F; - do{ - (*value_size)++; - value = (value << 7) + ((c = fgetc(fp)) & 0x7F); - } while (c & 0x80); - } - return(value); -} - - -//// -// Swap 4 bytes -int swap_endian_32(int big_endian){ - register int little_endian; - little_endian = (big_endian & 0x000000FF); - little_endian = ((big_endian & 0x0000FF00) >> 0x08) | (little_endian << 0x08); - little_endian = ((big_endian & 0x00FF0000) >> 0x10) | (little_endian << 0x08); - little_endian = ((big_endian & 0xFF000000) >> 0x18) | (little_endian << 0x08); - return(little_endian); -} - - -//// -// Swap 2 bytes -short swap_endian_16(short big_endian){ - register short little_endian; - little_endian = (big_endian & 0x00FF); - little_endian = ((big_endian & 0xFF00) >> 0x08) | (little_endian << 0x08); - return(little_endian); -} - -} diff --git a/utils/midi/source/track.cpp b/utils/midi/source/track.cpp deleted file mode 100644 index 3cc636d..0000000 --- a/utils/midi/source/track.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "track.h" - -namespace Midi { - -Track::Track(void){ - m_header.cookie[0] = m_header.cookie[1] = m_header.cookie[2] = m_header.cookie[3] = 0; - m_header.size = 0; - m_total_size = 0; -} - - -void Track::init_from_file(FILE *fp){ - Event *track_event; - unsigned int bytes_read = 0; - unsigned char last_status = 0x0; - - fread(&m_header, sizeof(midi_track_header_t), 1, fp); - m_header.size = swap_endian_32(m_header.size); - if(m_header.size == 0) return; - - while(bytes_read < m_header.size){ - track_event = new Event(); - track_event->init_from_file(fp, last_status); - m_events.push_back(track_event); - last_status = track_event->status(); - bytes_read += track_event->bytes_read(); - } -} - -Track::~Track(void){ - // Free all the track events - for(unsigned int i = 0; i < m_events.size(); i++){ - delete m_events[i]; - } -} - -}