How to do this is explained in the Macro API and MacroContext API sections of the SugarCube documentation. So, to correct your above code, you should do:
Macro.add('macroname', {
handler: function() {
var html = "";
html += "param[0] "+this.args[0]+"<br>";
html += "param[1] "+this.args[1]+"<br>";
html += "param[2] "+this.args[2]+"<br>";
$(this.output).wiki(html);
}
});
So then if you did "<<macroname 1 2 3>>" it would output:
param[0] 1
param[1] 2
param[2] 3
As for displaying the contents of a macro, you have to add "tags" to define names for any blocks of content you want to have inside the macro, or "null" if you only want the one main block. Then to get the code block's contents you use "this.payload[N].contents", where "N" is the number of the block of content (starting with zero). You can use "this.payload.length" to determine how many blocks of content the code has within that macro. For a simple example, modifying the above code to include a content block gives you this code:
Macro.add('macroname', {
tags: null,
handler: function() {
var html = "";
html += "param[0] "+this.args[0]+"<br>";
html += "param[1] "+this.args[1]+"<br>";
html += "param[2] "+this.args[2]+"<br>";
html += "content: "+this.payload[0].contents+"<br>";
$(this.output).wiki(html);
}
});
So then if you did "<<macroname 1 2 3>>test<</macroname>>" it would output:
param[0] 1
param[1] 2
param[2] 3
content: test
You could add multiple content blocks by adding an array of strings to the "tags" parameter in the definition, instead of using "null" there, and you can tell what tag name is being used in a code block by checking "this.payload[N].name" and any parameters passed in to that tag by checking the values in the "this.payload[N].args" array.
Hope that helps! :-)