SH101 style sequencer as ruby UniMIDI script

I came across a youtube video which shows an Axoloti set up to emulate an SH101 style sequencer being trigged by an incoming note.

I had not seen this style of sequencer before, and was interested in the patterns that can be generated particularly when you have say a 7 step sequence that has 8 triggers per bar, so the resulting pattern kind of slides around the main rhythm.

Since I don’t yet have an Axoloti I put toget a UniMIDI script to get the same result. I use this with my Electribe 2 Sampler, which is connected via USB to my Macbook running the script.

On the E2S I have a pattern set up as follows:
Part 1 – this is set so Osc = ‘Audio In Mono’ – which makes it effectively muted. The E2S sequencer has every step to this part off. Keys pressed on this part get sent out on Midi Channel 1.

Part 2 – this is set to one of the built in wave forms (not a sample). Also, the ‘AMP EG’ must be selected with a highish decay/release value. The E2S sequencer has every step to this part off. The decay/release is needed because the incoming notes will have a not off immediately after the note on, so the actual audible duration depends on the decay time. This is also why samples don’t work too well, the envelope generator only works how it needs to for the built in waveforms.

Parts 3-15 – Set up with whatever Osc and pattern you like.

Part 16 – this is set so Osc = ‘Audio In Mono’ – which makes it effectively muted. Any note sent on this channel (e.g. via the E2S sequenver) will act as a trigger for the SH101 style sequencer.

To use:

Set up the E2S as above. Then go to ‘part 1’ and press the ‘IFX on’ button so it lights red. This puts the SH101 script into ‘record mode’. Any notes you play on part 1 in this mode get added to the sequence (and also echoed back on Channel 2). Press ‘IFX On’ again (so light goes off) to put the SH101 sequencer into ‘play’ mode. Now go to ‘part 16’ and turn on/off whatever steps you want to trigger a note. Press play.

Going to ‘part 1’ and pressing another note key while in play mode will transpose the current sequence.

—-

!– HTML generated using hilite.me –>

#!/usr/bin/ruby

dir = File.dirname(File.expand_path(__FILE__))
$LOAD_PATH.unshift dir + "/../lib"

require "unimidi"

IN_CHANNEL=1
TRIGGER_CHANNEL=16
OUT_CHANNEL=2
REC_MODE_CC=0x68	#on my electribe 2, this is triggered by pressing the 'IFX ON' button.

DEBUG_LEVEL=1	#0=silent,1=major events, 2=all events

@input = UniMIDI::Input.gets
@output = UniMIDI::Output.gets

@rec_mode=false
@seq_length=0
@seq=[]
@seq_ptr=0
@transpose_amount=0

def debug_log (s,level)
	puts s unless level>DEBUG_LEVEL
end
def execute_status(s)
	debug_log("#{"%2X" % s[0]},#{s[1]},#{s[2]}",2)
	if ((s[0])==0xb0+IN_CHANNEL-1) && (s[1]==REC_MODE_CC)then
		new_rec_mode=(s[2]>64)
		if (@rec_mode==false) && (new_rec_mode==true) then
			@seq=[]
			@seq_length=0
			@seq_ptr=0
		end

		@rec_mode=new_rec_mode	
		debug_log("REC_MODE: #{@rec_mode}",1)
	end

	if (s[0]==0x90+IN_CHANNEL-1) && (s[2]>1)then #it's a note-on event on input channel

		if (@rec_mode) then
			@seq[@seq_length]=s[1]
			@seq_length+=1
			@output.puts(0x8F+OUT_CHANNEL, s[1],  64) #echo so can hear what was just played
			@output.puts(0x8F+OUT_CHANNEL, s[1],  0) #immediate note off 

			debug_log("added note - seq length #{@seq_length}",1)
		end

		if (!@rec_mode) && (@seq_length>0) && (IN_CHANNEL!=TRIGGER_CHANNEL) then # not recording or a trigger so do a transpose
			@transpose_amount=s[1]-@seq[0]
			debug_log("transpose amount now - #{@transpose_amount}",1)
		end

	end
	if (s[0]==0x90+TRIGGER_CHANNEL-1) && (s[2]>1)then #it's a note-on event on trigger channel
		if (!@rec_mode) then	
			if @seq_length>1 then
				debug_log( "play note - seq ptr #{@seq_ptr}",1)
				@output.puts(0x8F+OUT_CHANNEL, @seq[@seq_ptr]+@transpose_amount,  64) 
				@output.puts(0x8F+OUT_CHANNEL, @seq[@seq_ptr]+@transpose_amount,  0) #immediate note off 
				@seq_ptr+=1
				@seq_ptr=0 if @seq_ptr==@seq_length
			else
				debug_log("skipping - no sequence",1)
			end

		end

	end

end

status=[]

begin	

	loop do 	

				m = @input.gets_data
				m.each do |b|
					next if b==0xf8	#ignore clock beats
					if (b<0x80) then
						status<<b
					else
						status=[b]
					end					

					if status.length==3 then
						execute_status(status)
						cmd_byte=status[0]#keep original first byte for short form commands
						status=[cmd_byte] 
					end
				end	
	end
ensure
	@output.puts(0xAF+OUT_CHANNEL, 0x7B, 0) #all notes off
end

Kipper BASIC updates

Goog from CommodoreServer.com reported a couple of bugs in Kipper BASIC which I have fixed in version 1.21

The bugs were

  • Any UDP broadcast anywhere on the LAN (say, a DHCP client booting up, or the  ‘Host Announcements’ every Windows machine sends every few minutes) would result in TCPSEND or POLL reporting an error
  • If you had a variable (say X%) and you added a constant to it that happened to be the same as the current value of that variable (i.e. you had  X% =X%+7 when X% was already 7) you got a completely bogus number.

peekbot is back

I’ve turned on peekbot again, although I have disabled the ‘screendump’ feature (which would render a text file using the default font and screen colours from whatever system the text file appeared to originate from) – I think this was causing most of the crashes last time I had peekbot running.

I am also running peekbot via mongrel, on a non-standard port, although I have mod_rewrite rules in place such that any old links should get directed to the right spot.

 

On the trail of a Real Programmer

Coders of a certain vintage will be familiar with the story of Mel, the Real Programmer in which Ed Nather (writing in 1983) relates his experience from 20 years previously in attempting to patch a BlackJack program written by his ex-colleague (Mel Kaye) for the ancient RPC-4000 computer, and how the complexity of the code he had to work through left him so impressed with Mel’s familiarity with the inner workings of the computer that he abandoned the attempt to patch the code and even 20 years later, he still held Mel as the archetype of a “Real Programmer”.

I first came across the story on a BBS sometime the early 90s, at a time when I was just starting to become competent with x86 assembly code, and self-modifying code was a pretty fascinating idea, as was extreme performance optimisation.

Then recently I came across the Mel story again, and went looking to see what (if anything) of the legendary machines and codes had made their way into the retro-computing archives across the net. The ‘holy grail’ would be a copy of the original BlackJack program with the back-to-front cheat option, and major bragging rights would come from being able to make the patch that Ed Nather had given up looking to make.

I’m not there yet.

What I have found so far is:

From looking at the opcodes in the programming manual, I believe that the loop in question must have been based around opcode 21 “COMPARE MEMORY GREATER”, with the eventual ‘overflow’ to opcode 22 (TEST MINUS).

I don’t know what sort of work this loop would have been doing, although given the program documentation clearly states program execution starts at 00000, and that is also where (in the story) control is eventually transferred to once the loop exits, the loop must have been some kind of post-game cleanup, ready to re-start.

So my current theory is

  • the data stored in the upper memory locations is the card deck, (stored initially perhaps as numbers 1..52)
  • as cards were “dealt” they were marked as such by (e.g.) setting the sign bit
  • the loop without apparent exit is going through the pack removing the ‘dealt’ marker, prior to being shuffled at the start of a new game.

It is apparent that Ed’s memory was not completely accurate; not completely surprising given the 20 year gap from when the events occurred until when they were documented. He mentions the clue that helped him understand the way the loop exited as being the fact that the index register bit was set even though Mel never used the index register, and says this index register bit is “between the address and the operation code in the instruction word”. However reference manual is clear that the index register bit is the least significant bit, and in fact the opcode bits (0..4) are adjacent to the data address bits (5..16). I can’t see any way the index bit could have been part of the loop/overflow.

looking for stuck bits in ROM dumps

I have been playing around with disassembling some code that was apparently the bootstrap PROM for the SacState 8008 machine. The code was given to me as 8 BIN files, 256 bytes each, one for each PROM dump. One section of the code looked very odd, and I began to suspect a faulty read. One common error when reading old chips is a single pin can be shorted, or broken, such that every bit from that chip is read as the same value. So I put this script together to see if any of the files, every byte had the same value in 1 bit.

My hunch was correct – in one of the files, bit 5 was always 1, and that file coincided exactly with the very odd looking code. So to continue disassembling that file, I needed to select the value for bit 5 that made the most sense based on context.

#!/usr/bin/ruby
def abort(s)
	puts s
	Process.exit
end

abort "no filename specified" unless ARGV.length>0
ARGV.each do |filename|
	abort "#{filename} not found" unless File.exists?(filename)

	s=File.open(filename).read
	puts "#{filename}: #{s.length} bytes"
	8.times do |bit|
		bitmask= (1 << bit)
		set_found=false
		clear_found=false
		s.each_byte do |byte|
			set_found=true if (byte & bitmask >0)
			clear_found=true if (byte & bitmask ==0)
		end
		puts "bit #{bit} always 0 in file #{filename}" unless set_found
		puts "bit #{bit} always 1 in file #{filename}" unless clear_found

	end
	
end

BASIC ON BAILS

  • Do you want to harness your C64s awesome 1 MHz compute power to build state-of-the-art web applications?
  • Are you bored of OOP frameworks with structured exception handling and pine for a 40×25 IDE with good old fashioned GOTOs?
  • Do you wish you could claim 30 yrs experience with a web development language at the next coding job you apply for?

Then you need…. BASIC ON BAILS!

10 DHCP: REM SOCK ME SOME IP CONFIG, MOFO!
20 HTTPD 80, 100
100 !"<h1>LOOK MA! WEB STUFF!</h1>"
110 !"THIS MESSAGE WAZ BRUNG 2 U BY BASIC ON BAILS"
120 YIELD

Act now, we’ll chuck in for no extra charge Kipper BASIC : command line PING, TFTP, NETCAT, and other TCP socket goodness

Check it out…

ideservd for OSX

I recently acquired an IDE64. This is a cartridge for the C64 that lets you connect any sort of storage device with an IDE interface (typically a Compact Flash drive, but can also be an IDE hard drive or a CD-ROM). It also allows files to be transferred to/from a PC via USB.

Unfortunately the software that needs to run on the PC side (ideservd) only seems to be compiled for Windows, although the source code is available from the IDEDOS homepage. The source needed a bit of tweaking to compile on my macbook (OSX 10.6.4), specifically

  • deleting #include from ideserv.c
  • adding “-I /opt/local/include/” and ” -L/opt/local/lib/” to the gcc flags in the Makefile

Once it compiles, there’s some tricks to getting it to run.

First, it needs to be run under sudo, otherwise you will get a message saying “Failed changing root dir”.

Second, you need to disable the FTDIUSBSerialDriver that OSX has loaded automatically, otherwise you will get an error saying “unable to claim usb device. Make sure ftdi_sio is unloaded!”

Here’s a zip file containing the compiled binary and a wrapper script to load/unload FTDIUSBSerialDriver before/after running ideservd. – ideservd-0.24-osx.zip

RetroShamBo pt1

RoShamBo is another name for ‘paper scissors rock’.

RetroShamBo will be a retro network coding tournament. The date is still TBA, probably linked in with the next Retro-Challenge.