We are thinking that we should have packaged chips by the beginning of September. Either there will be 75% good chips, or 0% good chips. A single design error can render all chips useless. If there is any design error, let's hope it's one that doesn't leave the chip unable to be programmed.
Just wondering...
For the P1 wafers, do you know statistically where the good ~400 come from (ie their position on the wafers)?
Do you package all the ~524 before you can test for the ~400 good ones?
You're welcome.
We do have all die packaged first, then we test. We would love to do a wafer test, but to do is too expensive (money and time) to consider setting up that test at this time.
The packager doesn't give us any information about die placement per packaged part, so we're only going off of what we know from other's experience and technical knowledge shared in the industry to say that it's likely a high percentage of the good parts were near the center and further from the edge.
Chip, on the (hopefully improbable) chance that there are problems but they are not catastrophic, leaving the prototypes at least partially usable, is there any chance some of us who couldn't justify the investment in a FPGA board could get samples so we'd finally have something to work with?
The packager doesn't give us any information about die placement per packaged part
I realize it is far too late to do this for either Propeller, but I'm curious; given this limitation it would seem that a cheap workaround would be to give each chip on the die a different ID code which could be queried, so that the chips that function at all could announce what their die position was. This would also allow you to test something else I'm curious about, whether things like overclocking and temperature tolerances are affected by die placement. Is there any reason this would be unworkable?
Chip, on the (hopefully improbable) chance that there are problems but they are not catastrophic, leaving the prototypes at least partially usable, is there any chance some of us who couldn't justify the investment in a FPGA board could get samples so we'd finally have something to work with?
You bet. If it's at all usable, we can make little boards with the chips on them.
I realize it is far too late to do this for either Propeller, but I'm curious; given this limitation it would seem that a cheap workaround would be to give each chip on the die a different ID code which could be queried, so that the chips that function at all could announce what their die position was. This would also allow you to test something else I'm curious about, whether things like overclocking and temperature tolerances are affected by die placement. Is there any reason this would be unworkable?
This is not really possible, since the mask set is stepped along the wafer for lithography purposes, repeating the same image over and over. There is no way to introduce any unique patterns per chip.
It's 2:00am here and it looks like the Spin2 compiler and interpreter are working together now. I'm sure there are going to be more bugs to fix and things to add, but it's finally 'alive'.
Hopefully, I'll have a Spin2 version of PNUT.EXE to post in the next week, or so. I'll need to write some documentation to explain Spin2.
It's 2:00am here and it looks like the Spin2 compiler and interpreter are working together now. I'm sure there are going to be more bugs to fix and things to add, but it's finally 'alive'.
Hopefully, I'll have a Spin2 version of PNUT.EXE to post in the next week, or so. I'll need to write some documentation to explain Spin2.
Making it cache them would be really easy - have a long for each snippet, the bottom 9 bits holding the snippet's cogram address or 0 if it's not loaded, and the rest holding when this snippet was last used. Whenever a new snippet is needed and there isn't enough room for it, the oldest one would be unloaded and replaced with the new one.
Obviously, caching them cuts down on the amount of user PASM space you have, but maybe you could specify how much space you needed and it would make the cache size fill only the remainder?
Caching also reduces the efficiency of the code execution mechanism. The cache table has to be searched. Any saved (cached) snippet has to be copied within the cog since there are no relative jumps, etc. Caches are great when there's a high cost to fetch the code snippet, like from an SD card or EEPROM. It's pretty cheap to fetch a small block of code from hub RAM.
Caching also reduces the efficiency of the code execution mechanism. The cache table has to be searched. Any saved (cached) snippet has to be copied within the cog since there are no relative jumps, etc. Caches are great when there's a high cost to fetch the code snippet, like from an SD card or EEPROM. It's pretty cheap to fetch a small block of code from hub RAM.
Mike's right. The hassle of determining whether or not what you wanted was already cached would take longer than just loading what are often 4-long snippets (using RDLONGC, which caches). This is where some kind of content-addressable memory subsystem would help a lot.
Mike's right. The hassle of determining whether or not what you wanted was already cached would take longer than just loading what are often 4-long snippets (using RDLONGC, which caches). This is where some kind of content-addressable memory subsystem would help a lot.
Hi Chip,
Are you planning to document the instruction set used by the Spin interpreter so that it could be used as a target for other languages?
Are you planning to document the instruction set used by the Spin interpreter so that it could be used as a target for other languages?
Thanks,
David
I haven't thought about it. Do you think documentation would be necessary for someone to use the code? I figure they'd probably modify it in some ways after figuring out how it works. The resident portion of the interpreter spans from $147 to $1F5, with some of that being variable space, so it's not that much to learn. Some instruction snippets push stuff and other instructions pop stuff, and some do both. It's RAM-based, so it's nothing that would have to be adhered to strictly.
I was thinking that the bytecodes could be made dynamic at compile time, so that only those used need be included in the interpreter. That would shorten the descriptor table and eliminate unused snippets. Right now the whole interpreter is just over 5KB. Most programs would use way less than that. The advantage of keeping the whole interpreter resident leaves the door open for dynamic overlays.
I would also hope that the Spin2 instruction set is documented so it could be used as a target for other languages. I would also put a "bid" in for similar documentation for the Prop1 Spin instruction set. I know a lot has been "reverse engineered" (Spin1), but it would be nice to have it organized and vetted / filled in for the same purpose.
Sure, most of the Spin2 interpreter is hub RAM based and there's some potential benefit in trimming down the instruction set on an application by application basis, but then there's no run-time commonality from application to application ... not a bad thing if you want to think of the interpreter like a run-time library with the Spin2 compiler managing the packaging of the interpreter with the program and the assumption at run-time that there will always be a native interpreter or other "main" native program that will be given control initially. This is opposite of what Spin has assumed on the Prop1, that there's a built-in Spin machine that gets control initially and can surrender control to one of the native processors.
I think the idea of multiple interpreters (for Spin2 or C or whatever) is fine, but the conceptual base then needs to shift to the native instruction set as the primary or initial program to be given control when booted.
I would also hope that the Spin2 instruction set is documented so it could be used as a target for other languages. I would also put a "bid" in for similar documentation for the Prop1 Spin instruction set. I know a lot has been "reverse engineered" (Spin1), but it would be nice to have it organized and vetted / filled in for the same purpose.
Sure, most of the Spin2 interpreter is hub RAM based and there's some potential benefit in trimming down the instruction set on an application by application basis, but then there's no run-time commonality from application to application ... not a bad thing if you want to think of the interpreter like a run-time library with the Spin2 compiler managing the packaging of the interpreter with the program and the assumption at run-time that there will always be a native interpreter or other "main" native program that will be given control initially. This is opposite of what Spin has assumed on the Prop1, that there's a built-in Spin machine that gets control initially and can surrender control to one of the native processors.
I think the idea of multiple interpreters (for Spin2 or C or whatever) is fine, but the conceptual base then needs to shift to the native instruction set as the primary or initial program to be given control when booted.
I think that as time goes by and the interpreter is improved, there will be many changes to it, making it a moving target for documentation. I think what you said about shifting focus to the native instruction set is the way to go. Spin2 is writ in water.
By packaging the interpreter as part of the run-time library and making the assumption that some kind of native program is what is initially started by the bootstrap, the bootstrap doesn't care what version of the Spin interpreter or C interpreter or whatever is packaged as part of the program. Our programming systems and operating systems don't care which run-time library is packaged with the program, only that the format of the program on the storage device is known and any calls to ROM are at permanently standard locations and follow documented calling conventions.
I would like to see better docs so that it could become part of a library to support other languages too. IOW I would like to see a BASIC frontend, and I would like to see a variant of Spin where indentation is not forced (ie using endif, endrepeat/endloop, endcase, or the horrid "{" and "}" from C etc.). Switching between the forced indentation and others should even be possible on the fly by extending the editor. Don't get me wrong, I love the enforced identation, but so many don't like it I think it would be great to provide an alternative.
The overhead of testing to see whether small "snippets" are already loaded is a large percentage of the load time, so as others have said, is detrimental to the timing and a waste of code space.
By being "soft", improvements can be made. Great times ahead.
This (identifying the chip position on wafer post hoc -- LR) is not really possible, since the mask set is stepped along the wafer for lithography purposes, repeating the same image over and over. There is no way to introduce any unique patterns per chip.
Fascinating... I suppose I always envisioned that wafer being exposed all at once by a single great big mask for each step. I am amazed at the multiple disciplines you had to master to make these chips real. I might have thought to design my own CPU and maybe give it cores but the PLL's and counter-timers are genius totally out of my pay grade and the translation to IC mask HEAD EXPLODES.
As far as ID'ing the location of each die on a wafer, everything comes at a cost. Laser etching is an option but not cost effective for our operation. Expanding that, the laser etching could simply be an enumerated mark on the individual dies or strategically aligned to readable fuses from the chip itself during normal operation of the chip, but again cost prohibitive to our focused efforts.
As far as ID'ing the location of each die on a wafer, everything comes at a cost. Laser etching is an option but not cost effective for our operation. Expanding that, the laser etching could simply be an enumerated mark on the individual dies or strategically aligned to readable fuses from the chip itself during normal operation of the chip, but again cost prohibitive to our focused efforts.
If you did wafer probe then you could burn fuses to uniquely id each die during test, and keep a record of their locations. Some companies do this. (I could see the military wanting tracking like that, but have no experience with miltary electronics.) But it doesn't always make sense to do wafer probe even if you're setup to do it. Someone needs to run the numbers to see if it pays off. (e.g. not packaging bad parts.) For WLBGAs you typically test in wafer form (and hopefully several die in parallel), and then typically no testing is done after dicing the wafer. The chips that I've worked on lately are WLBGAs, and you can see the ATE pass/fail results mapped onto an image of the wafer.
With the bigger address space, SPIN2 can become a general purpose programming language.
That gives me a wonderfully evil idea. Retarget Spin for the XMOS devices so those poor users have something easier to work with:)
How do you imagine this working:
1) Compile Spin to native code for your target, say x86, ARM etc? Like a C compiler does.
2) Compile Spin to the standard byte codes and have a native, x86, ARM.., program interpret them? Like a JVM.
One thing that worries me with Spin as a general purpose language is that I'm used to the tight and seamless integration of the high level Spin syntax with the low level PASM. So much so that in my mind that Spin is Spin + PASM. Which leads to:
3) Adopt the instruction set of the target machine for the PASM parts? Results in non-portable hence non-general purpose use.
4) Keep the to PASM instruction set? Requires a Prop simulator on the target.
One way of tracking yield of the different locations on the wafer could be to have the cut chips, then the packaged chips placed in a carrier with the same layout as the wafer. As long as the chips are always placed correctly, you'd know which position they came from.
Of course, no one will do that...
The other option is to make certain that when the wafer is cut, that the individual chips are always picked in the same order, and that they use a new tray for each wafer.
(This assumes that when the chips are packaged, they're always picked in a predictable order off the tray, and also placed in a known order on the output tray)
The problem is that it'll probably cost too much in man-hours to plan and verify this to make it worthwhile.
(Unless there's one or more chips with the same faults from every wafer)
Because the main struggle in a wafer fab is to get yields up to an acceptable level. Information on the location and type of defects is vital to improving each process step. And almost all processing steps (25-30 times lithography, 10-15 times material deposition) have defects and inaccuracies that have a distribution over the the wafer surface. If a production line exhibits bad yields a team of process engineers will step in, try to pin down the problem ( "process step 6 aluminium oxyde deposition has a too low thickness on the top right side of the wafer") and adjust the equipment for that step.
Comments
Singular: Die
Plural; Dice or Dies
Looks like we'll all know in a couple weeks.
Jeff
You're welcome.
We do have all die packaged first, then we test. We would love to do a wafer test, but to do is too expensive (money and time) to consider setting up that test at this time.
The packager doesn't give us any information about die placement per packaged part, so we're only going off of what we know from other's experience and technical knowledge shared in the industry to say that it's likely a high percentage of the good parts were near the center and further from the edge.
I realize it is far too late to do this for either Propeller, but I'm curious; given this limitation it would seem that a cheap workaround would be to give each chip on the die a different ID code which could be queried, so that the chips that function at all could announce what their die position was. This would also allow you to test something else I'm curious about, whether things like overclocking and temperature tolerances are affected by die placement. Is there any reason this would be unworkable?
You bet. If it's at all usable, we can make little boards with the chips on them.
This is not really possible, since the mask set is stepped along the wafer for lithography purposes, repeating the same image over and over. There is no way to introduce any unique patterns per chip.
Hopefully, I'll have a Spin2 version of PNUT.EXE to post in the next week, or so. I'll need to write some documentation to explain Spin2.
Here's the interpreter:
Spin2interpreter.spin
CON int_org = $147 ' ' '*************** '* Interpreter * '*************** ' DAT orgh $E80 org _vbase long 0 'vbase (nop) - modified by compiler _pbase long 0 'pbase (nop) - modified by compiler _pcurr long 0 'pcurr (nop) - modified by compiler _dlocs long 0 'dcurr (nop) - modified by compiler reps #int_res-int_org,#1 'move interpreter into position setinds #int_res-1,#int_obj+int_res-int_org-1 mov indb--,inda-- reps #$200-int_res,#1 'clear high regs (leaves indb at $000) setindb #int_res mov indb++,#0 reps #$100,#1 'clear stack endaddr long @endcode '(nop) pushbr #0 pushbr endaddr 'push return pcurr, points to 'cogstop(cogid)' subspb #3 'push 3 locations (0's) getspb dbase 'set dbase subspb _dlocs 'set dcurr mov vbase,_vbase 'set vbase mov pbase,_pbase 'set pbase setptrb _pcurr 'set pcurr jmp #a 'clear low regs, start interpreter endcode byte (@op_get - @descriptors) >> 2, $01 'cogid byte (@op_set1 - @descriptors) >> 2, $03 'cogstop ' ' ' Interpreter start ' int_obj org int_org ' ' ' Constants/variables ' descriptor_base long @descriptors h003C0000 long $003C0000 'conditional field bits hFFFFFFFF long $FFFFFFFF 'all bits callwrites call #writes callwriter call #writer a reps #int_org,#1 'initially executes, becomes 'a' h0001FFF0 long $0001FFF0 'vbase/pbase mask (nop) b mov indb++,#0 'initially executes, becomes 'b' jmpback jmp #getbyte ' ' ' Pre-move pops and check ' premove popbr a popbr y popbr x tjz a,#getbyte sub a,#1 premove_ret ret ' ' ' Get sign-extended byte (c=0), used to make -$80..+$7F offset ' Get and append word to x (c=1), used to make $0xxxx or $1xxxx offset ' xword rdbytec y,ptrb++ shl x,#8 or x,y if_c rdbytec y,ptrb++ if_c shl x,#8 if_c or x,y if_nc shl x,#24 if_nc sar x,#24 xword_ret ret ' ' ' call obj.sub ' call obj.sub[] ' call obj[].sub ' call obj[].sub[] ' call sub ' call sub[] ' call ptr ' call_obj shl x,#2 'lookup obj vbase/pbase offsets add x,pbase rdlong x,x call_ptr rol x,#4 'set relative pbase add pbase,x and pbase,h0001FFF0 ror x,#16 'set relative vbase add vbase,x and vbase,h0001FFF0 call_sub shl y,#2 'lookup locals/pbase offsets add y,pbase rdlong y,y mov dbase,dcall 'set call dbase sub dbase,#3 setspb dcall 'get old dcall popbr dcall getptrb x 'set return pcurr pushbr x setptrb pbase 'set call pcurr addptrb y shr y,#32-4 'sub locals from dcurr subspb y jmp #getbyte 'loop to getbyte ' ' ' Bitfield addressing, pop and set bitfield parameters ' bitfield popbr bshift 'pop range lsb popbr bmask 'pop range msb and bshift,#$1F 'trim lsb/msb and bmask,#$1F cmp bmask,bshift wc 'reverse range? if_c xor bshift,bmask 'if reverse range, swap msb/lsb if_c xor bmask,bshift if_c xor bshift,bmask sub bmask,bshift 'get number of bits add bmask,#1 neg brev,bmask 'get rev count setbc brev,#5 'save reverse flag blmask bmask 'get bitlength mask shl bmask,bshift 'bmask = mask, brev = reverse, bshift = lsb offset bitfield_ret ret 'return ' ' ' Write register (addr = address, x = value) ' ' if OUTA..OUTD then update PINA..PIND ' if DRVA..DRVD then update DIRA..DIRD ' writer movd :mov,addr and addr,#$1FF 'check for OUTA..OUTD/DRVA..DRVD cmp addr,#$1E8 wc if_nc cmpr addr,#$1EF wc :mov if_c mov $000,x 'normal register if_c jmp writer_ret movs :xors,addr 'set OUTA..OUTD/DRVA..DRVD addresses movd :xord,addr movd :xor,addr setb :xor,#9+4 :xors xor x,$1E8 'update OUTx/DRVx shadow register :xord xor $1E8,x :xor xor $1F8,x 'update PINx/DIRX atomically writer_ret ret ' ' ' Write stack/local (addr = address, x = value) ' writes getspb y setspb addr pushb x setspb y writes_ret ret ' ' ' Do bitfield read (x = value) ' readb and x,bmask shr x,bshift if_c rev x,brev pushbr x jmp #getbyte ' ' ' Do bitfield write (bval = value, writeb_i = write instruction) ' writeb popbr x 'pop value to write if_c rev x,brev 'reverse bits? shl x,bshift 'shift into position and x,bmask 'mask bits andn bval,bmask 'mask away original bits or x,bval 'merge bits writeb_i call #writer 'call #writer / call #writes / wrxxxx x,addr writeb_ret jmp #getbyte ' ' ' Do bitfield assignment (bval = value, assignb_i = write instruction, mask = sized mask) ' assignb_long mov mask,hFFFFFFFF 'set long mask assignb movs jmpback,#assignb_ret 'set jmpback address to assignb_ret mov x,bval 'save original value and x,bmask 'extract bitfield for operation shr x,bshift if_c rev x,brev jmp #assign_op 'get math operator and execute assignb_ret testb brev,#5 wc 'snippet returns to here, get reverse flag into c call #writeb 'write bitfield movs writeb_ret,#getbyte 'restore jump to getbyte and x,mask 'mask it and x,bmask 'extract bitfield for pushing shr x,bshift if_c rev x,brev jmp #assign_p 'push it (optional) ' ' ' Do assignment (x = value, assign_i = write instruction, mask = sized mask) ' assign_long mov mask,hFFFFFFFF 'set long mask assign movs jmpback,#assign_ret 'set jmpback address to :ret assign_op rdbytec a,ptrb++ 'get math operator and flags setzc a wz,wc 'load flags, operator already in position muxz assign_p,h003C0000 'set push condition to z if_c popbr y 'swap args? pushbr x if_c pushbr y jmp #hotbyte 'load and execute math snippet assign_ret popbr x 'snippet returns to here, pop value assign_i call #writer 'call #writer / call #writes / wrxxxx x,addr and x,mask 'mask it assign_p pushbr x 'push it (optional) movs jmpback,#getbyte 'replace jmpback address, getbyte next ' ' ' Get byte code, look up descriptor long, load code longs in-line, execute, repeat ' getbyte rdbytec a,ptrb++ ' get byte code shl a,#2 ' shift to make long offset hotbyte add a,descriptor_base ' add base address of descriptor longs getptrb y ' save ptrb rdlong x,a '..2 get descriptor long movd :reps,x '3 descriptor[8..0] holds code longs minus 1 shr x,#7 '4 descriptor[23..7] is code offset setptrb x '5 point PTRB to code longs :reps reps #1,#1 '6 ready for fast in-line code load setindb #snippet '7 point INDB to in-line code rdlongc indb++,ptrb++ '0.. load code longs in-line using cached read setptrb y ' restore ptrb mov indb++,jmpback ' finish in-line code with 'jmp #getbyte' shr x,#25-7 wz,wc ' descriptor[31..25] is a value, [24] is C ' ' ' Reserved registers ' int_res snippet res 18 ' executable in-line code snippet, loops x res 1 y res 1 dcall res 1 dbase res 1 vbase res 1 pbase res 1 _outa res 1 '@$1E8 OUTA..OUTD _outb res 1 '@$1E9 (writes update PINA..PIND at $1F8..$1FB) _outc res 1 '@$1EA _outd res 1 '@$1EB _drva res 1 '@$1EC DRVA..DRVD _drvb res 1 '@$1ED (writes update DIRA..DIRD at $1FC..$1FD) _drvc res 1 '@$1EE _drvd res 1 '@$1EF bshift res 1 '@$1F0 bitfield addressing bmask res 1 '@$1F1 brev res 1 '@$1F2 bval res 1 '@$1F3 addr res 1 '@$1F4 assignment addressing mask res 1 '@$1F5 ' ' '************************ '* Bytecode Descriptors * '************************ ' descriptors ' variable modifiers (14) op_writep long %0000000_0 << 24 + @_clr << 7 + 0 'write w/push %10 push op_rep long %0000000_0 << 24 + @_rep << 7 + 12 'repeat var %00 no op_clr long %0000000_0 << 24 + @_clr << 7 + 1 'clear %00 no op_clrpost long %0000000_0 << 24 + @_clrpost << 7 + 0 'clear post %00 push op_set long %0000000_0 << 24 + @_set << 7 + 1 'set %00 no op_setpost long %0000000_0 << 24 + @_setpost << 7 + 0 'set post %00 push op_incpre long %0000000_0 << 24 + @_incdec << 7 + 3 'inc pre / inc %10 push / %00 no op_incpost long %0000001_0 << 24 + @_incdec << 7 + 3 'inc post %00 push op_decpre long %0000000_1 << 24 + @_incdec << 7 + 3 'dec pre / dec %10 push / %00 no op_decpost long %0000001_1 << 24 + @_incdec << 7 + 3 'dec post %00 push op_incmod long %0000000_0 << 24 + @_idmod << 7 + 6 'incmod %00 no op_incmodp long %0000001_0 << 24 + @_idmod << 7 + 6 'incmod w/push %00 push op_decmod long %0000000_1 << 24 + @_idmod << 7 + 6 'decmod %00 no op_decmodp long %0000001_1 << 24 + @_idmod << 7 + 6 'decmod w/push %00 push ' unaries (27) op_notb long %0000000_0 << 24 + @_notb << 7 + 2 'NOT %x0 op_not long %0100101_0 << 24 + @_una << 7 + 4 '! %x0 op_neg long %0000000_0 << 24 + @_neg << 7 + 2 '- %x0 op_abs long %0000000_0 << 24 + @_abs << 7 + 2 '|| %x0 op_enc long %0000000_0 << 24 + @_enc << 7 + 2 '>| %x0 op_decod2 long %0100000_0 << 24 + @_una << 7 + 4 'DECOD2(x) %x0 op_decod3 long %0100001_0 << 24 + @_una << 7 + 4 'DECOD3(x) %x0 op_decod4 long %0100010_0 << 24 + @_una << 7 + 4 'DECOD4(x) %x0 op_decod5 long %0100011_0 << 24 + @_una << 7 + 4 'DECOD5(x), |< %x0 op_blmask long %0100100_0 << 24 + @_una << 7 + 4 'BLMASK(x) %x0 op_onecnt long %0100110_0 << 24 + @_una << 7 + 4 'ONECNT(x) %x0 op_zercnt long %0100111_0 << 24 + @_una << 7 + 4 'ZERCNT(x) %x0 op_incpat long %0101000_0 << 24 + @_una << 7 + 4 'INCPAT(x) %x0 op_decpat long %0101001_0 << 24 + @_una << 7 + 4 'DECPAT(x) %x0 op_bingry long %0101010_0 << 24 + @_una << 7 + 4 'BINGRY(x) %x0 op_grybin long %0101011_0 << 24 + @_una << 7 + 4 'GRYBIN(x) %x0 op_mergew long %0101100_0 << 24 + @_una << 7 + 4 'MERGEW(x) %x0 op_splitw long %0101101_0 << 24 + @_una << 7 + 4 'SPLITW(x) %x0 op_seussf long %0101110_0 << 24 + @_una << 7 + 4 'SEUSSF(x) %x0 op_seussr long %0101111_0 << 24 + @_una << 7 + 4 'SEUSSR(x) %x0 op_sqrt long %0000000_0 << 24 + @_sqrt << 7 + 4 'SQRT(x) %x0 op_qlog long %0000000_0 << 24 + @_qlogexp << 7 + 5 'QLOG(x) %x0 op_qexp long %0000000_1 << 24 + @_qlogexp << 7 + 5 'QEXP(x) %x0 op_rndf long %0000000_0 << 24 + @_rnd << 7 + 8 'RNDF(x) %x0 op_rndr long %0000000_1 << 24 + @_rnd << 7 + 8 'RNDR(x) %x0 op_subcnt long %0001100_0 << 24 + @_una << 7 + 4 'SUBCNT(x) %x0 op_getpix long %0000000_0 << 24 + @_getpix << 7 + 4 'GETPIX(x) %x0 ' binaries (23) op_andb long %0000000_0 << 24 + @_andorb << 7 + 4 'AND %x1 op_orb long %0000000_1 << 24 + @_andorb << 7 + 4 'OR %x1 op_xorb long %0000000_0 << 24 + @_xorb << 7 + 5 'XOR %x1 op_andn long %0011001_0 << 24 + @_bin << 7 + 5 '&! %x1 op_and long %0011000_0 << 24 + @_bin << 7 + 5 '& %x1 op_or long %0011010_0 << 24 + @_bin << 7 + 5 '| %x1 op_xor long %0011011_0 << 24 + @_bin << 7 + 5 '^ %x1 op_ror long %0001000_0 << 24 + @_bin << 7 + 5 '-> %x1 op_rol long %0001001_0 << 24 + @_bin << 7 + 5 '<- %x1 op_shr long %0001010_0 << 24 + @_bin << 7 + 5 '>> %x1 op_shl long %0001011_0 << 24 + @_bin << 7 + 5 '<< %x1 op_sar long %0001110_0 << 24 + @_bin << 7 + 5 '~> %x1 op_sal long %0000000_0 << 24 + @_sal << 7 + 5 '<~ %x1 op_rev long %0000000_0 << 24 + @_rev << 7 + 4 '>< %x1 op_min long %0010000_0 << 24 + @_bin << 7 + 5 '#> %x1 op_max long %0010001_0 << 24 + @_bin << 7 + 5 '<# %x1 op_add long %0100000_0 << 24 + @_bin << 7 + 5 '+ %x1 op_sub long %0100001_0 << 24 + @_bin << 7 + 5 '- %x1 op_mul long %0000000_0 << 24 + @_mul << 7 + 6 '* %x1 op_scl long %0000000_0 << 24 + @_scl << 7 + 6 '** %x1 op_div long %0000000_0 << 24 + @_divx << 7 + 7 '/ %x1 op_rem long %0000001_0 << 24 + @_divx << 7 + 7 '// %x1 op_fra long %0000000_0 << 24 + @_fra << 7 + 7 '*/ %x1 ' equality tests (6) op_b long %0000000_0 << 24 + @_m << 7 + 6 '< op_a long %0000000_1 << 24 + @_m << 7 + 6 '> op_ne long %0000000_0 << 24 + @_e << 7 + 4 '<> op_eq long %0000000_1 << 24 + @_e << 7 + 4 '== op_be long %0000001_1 << 24 + @_m << 7 + 6 '=< / <= op_ae long %0000001_0 << 24 + @_m << 7 + 6 '=> / >= ' math terms (8) op_quo64 long %0000000_0 << 24 + @_divh64 << 7 + 10 'QUO64(al,ah,b) op_quo64u long %0000000_1 << 24 + @_divh64 << 7 + 10 'QUO64U(al,ah,b) op_rem64 long %0000001_0 << 24 + @_divh64 << 7 + 10 'REM64(al,ah,b) op_rem64u long %0000001_1 << 24 + @_divh64 << 7 + 10 'REM64U(al,ah,b) op_sqrt64 long %0000000_0 << 24 + @_sqrt64 << 7 + 6 'SQRT64(l,h) op_negb long %0000000_0 << 24 + @_negb << 7 + 3 'NEGB(x,bool) op_muxb long %0000000_0 << 24 + @_muxb << 7 + 4 'MUXB(x,y,bool) op_ternary long %0000000_0 << 24 + @_ternary << 7 + 4 'select ? true : false ' math procedures (9) op_mul64 long %0000000_0 << 24 + @_mul64 << 7 + 9 'MUL64(a,b : l,h) op_mul64u long %0000000_1 << 24 + @_mul64 << 7 + 9 'MUL64U(a,b : l,h) op_div64 long %0000000_0 << 24 + @_div64 << 7 + 11 'DIV64(al,ah,b : q,r) op_div64u long %0000000_1 << 24 + @_div64 << 7 + 11 'DIV64U(al,ah,b : q,r) op_qsincos long %0000000_0 << 24 + @_qtrig << 7 + 12 'QSINCOS(r,t : x,y) op_qarctan long %0000001_0 << 24 + @_qtrig << 7 + 12 'QARCTAN(x,y : r,t) op_qrotate long %0000000_1 << 24 + @_qtrig << 7 + 12 'QROTATE(x,y,t : x,y) op_getacca long %0000000_0 << 24 + @_getacca << 7 + 3 'GETACCA(l,h) op_getaccb long %0000000_0 << 24 + @_getaccb << 7 + 3 'GETACCB(l,h) ' constants (9) op_con1n long %0000000_1 << 24 + @_conxn << 7 + 1 'constant -1 op_con0 long %0000000_0 << 24 + @_conxp << 7 + 0 'constant 0 op_con1 long %0000001_0 << 24 + @_conxp << 7 + 0 'constant 1 op_con8p long %0000000_0 << 24 + @_con8 << 7 + 2 'constant $000000xx +1 byte op_con8n long %0000000_1 << 24 + @_con8 << 7 + 2 'constant $FFFFFFxx +1 op_conexp long %0000000_0 << 24 + @_conexp << 7 + 4 'constant 2^n,dec,not +1 op_con16p long %0000000_0 << 24 + @_con16 << 7 + 5 'constant $0000xxxx +2 op_con16n long %0000000_1 << 24 + @_con16 << 7 + 5 'constant $FFFFxxxx +2 op_con32 long %0000000_0 << 24 + @_con32 << 7 + 5 'constant $xxxxxxxx +4 ' inter-memory moves (19) op_reg2reg long %0000000_0 << 24 + @_reg2reg << 7 + 10 'REG_TO_REG(@reg,@reg,longs) op_reg2stk long %0000000_0 << 24 + @_reg2stk << 7 + 7 'REG_TO_STK(@reg,@stk,longs) op_reg2loc long %0000000_0 << 24 + @_reg2loc << 7 + 8 'REG_TO_LOC(@reg,@loc,longs) op_reg2mem long %0000000_0 << 24 + @_reg2mem << 7 + 7 'REG_TO_MEM(@reg,@mem,longs) op_stk2reg long %0000000_0 << 24 + @_stk2reg << 7 + 7 'STK_TO_REG(@stk,@reg,longs) op_stk2stk long %0000000_0 << 24 + @_stk2stk << 7 + 14 'STK_TO_STK(@stk,@stk,longs) op_stk2loc long %0000000_0 << 24 + @_stk2loc << 7 + 11 'STK_TO_LOC(@stk,@loc,longs) op_stk2mem long %0000000_0 << 24 + @_stk2mem << 7 + 8 'STK_TO_MEM(@stk,@mem,longs) op_loc2reg long %0000000_0 << 24 + @_loc2reg << 7 + 8 'LOC_TO_REG(@loc,@reg,longs) op_loc2stk long %0000000_0 << 24 + @_loc2stk << 7 + 11 'LOC_TO_STK(@loc,@stk,longs) op_loc2loc long %0000000_0 << 24 + @_loc2loc << 7 + 16 'LOC_TO_LOC(@loc,@loc,longs) op_loc2mem long %0000000_0 << 24 + @_loc2mem << 7 + 8 'LOC_TO_MEM(@loc,@mem,longs) op_mem2reg long %0000000_0 << 24 + @_mem2reg << 7 + 7 'MEM_TO_REG(@mem,@reg,longs) op_mem2stk long %0000000_0 << 24 + @_mem2stk << 7 + 9 'MEM_TO_STK(@mem,@stk,longs) op_mem2loc long %0000000_0 << 24 + @_mem2loc << 7 + 10 'MEM_TO_LOC(@mem,@loc,longs) op_mem2mem long %0000010_0 << 24 + @_xmove << 7 + 15 'MEM_TO_MEM(@mem,@mem,longs), LONGMOVE(from,to,count) op_bytemove long %0000000_0 << 24 + @_xmove << 7 + 15 'BYTEMOVE(from,to,count) op_wordmove long %0000001_0 << 24 + @_xmove << 7 + 15 'WORDMOVE(from,to,count) op_quadmove long %0000100_1 << 24 + @_qmove << 7 + 9 'QUADMOVE(from,to,count) ' register reads/writes/assigns (6) op_rdreg long %0000000_0 << 24 + @_rdreg << 7 + 4 'read register op_wrreg long %0000000_0 << 24 + @_wrreg << 7 + 2 'write register op_asreg long %0000000_0 << 24 + @_asreg << 7 + 5 'assign register op_rdregb long %0000000_0 << 24 + @_rdregb << 7 + 6 'read register bitfield op_wrregb long %0000000_0 << 24 + @_wrregb << 7 + 6 'write register bitfield op_asregb long %0000001_0 << 24 + @_wrregb << 7 + 7 'assign register bitfield ' stack reads/writes/assigns (6) op_rdstk long %0000000_0 << 24 + @_rdstk << 7 + 5 'read stack op_wrstk long %0000000_0 << 24 + @_wrstk << 7 + 5 'write stack op_asstk long %0000000_0 << 24 + @_asstk << 7 + 8 'assign stack op_rdstkb long %0000000_0 << 24 + @_rdstkb << 7 + 6 'read stack bitfield op_wrstkb long %0000000_0 << 24 + @_wrstkb << 7 + 9 'write stack bitfield op_asstkb long %0000001_0 << 24 + @_wrstkb << 7 + 10 'assign stack bitfield ' local reads/writes/assigns (30) op_rdloc0 long %0000000_0 << 24 + @_rdlocx << 7 + 5 'read local 0 op_rdloc1 long %0000001_0 << 24 + @_rdlocx << 7 + 5 'read local 1 op_rdloc2 long %0000010_0 << 24 + @_rdlocx << 7 + 5 'read local 2 op_rdloc3 long %0000011_0 << 24 + @_rdlocx << 7 + 5 'read local 3 op_rdloc4 long %0000100_0 << 24 + @_rdlocx << 7 + 5 'read local 4 op_rdloc5 long %0000101_0 << 24 + @_rdlocx << 7 + 5 'read local 5 op_rdloc6 long %0000110_0 << 24 + @_rdlocx << 7 + 5 'read local 6 op_wrloc0 long %0000000_0 << 24 + @_wrlocx << 7 + 5 'write local 0 op_wrloc1 long %0000001_0 << 24 + @_wrlocx << 7 + 5 'write local 1 op_wrloc2 long %0000010_0 << 24 + @_wrlocx << 7 + 5 'write local 2 op_wrloc3 long %0000011_0 << 24 + @_wrlocx << 7 + 5 'write local 3 op_wrloc4 long %0000100_0 << 24 + @_wrlocx << 7 + 5 'write local 4 op_wrloc5 long %0000101_0 << 24 + @_wrlocx << 7 + 5 'write local 5 op_wrloc6 long %0000110_0 << 24 + @_wrlocx << 7 + 5 'write local 6 op_asloc0 long %0000000_0 << 24 + @_aslocx << 7 + 7 'assign local 0 op_asloc1 long %0000001_0 << 24 + @_aslocx << 7 + 7 'assign local 1 op_asloc2 long %0000010_0 << 24 + @_aslocx << 7 + 7 'assign local 2 op_asloc3 long %0000011_0 << 24 + @_aslocx << 7 + 7 'assign local 3 op_asloc4 long %0000100_0 << 24 + @_aslocx << 7 + 7 'assign local 4 op_asloc5 long %0000101_0 << 24 + @_aslocx << 7 + 7 'assign local 5 op_asloc6 long %0000110_0 << 24 + @_aslocx << 7 + 7 'assign local 6 op_rdloc long %0000000_0 << 24 + @_rdloc << 7 + 7 'read local op_wrloc long %0000000_0 << 24 + @_wrloc << 7 + 7 'write local op_asloc long %0000000_0 << 24 + @_asloc << 7 + 9 'assign local op_rdloci long %0000000_1 << 24 + @_rdloci << 7 + 8 'read local indexed op_wrloci long %0000000_1 << 24 + @_wrloci << 7 + 8 'write local indexed op_asloci long %0000000_1 << 24 + @_asloci << 7 + 10 'assign local indexed op_rdlocb long %0000000_0 << 24 + @_rdlocb << 7 + 7 'read local bitfield op_wrlocb long %0000000_0 << 24 + @_wrlocb << 7 + 9 'write local bitfield op_aslocb long %0000001_0 << 24 + @_wrlocb << 7 + 10 'assign local bitfield ' memory reads/writes/assigns (18) op_rdbyte long %0000000_0 << 24 + @_rdbyte << 7 + 2 'read byte op_rdword long %0000000_0 << 24 + @_rdword << 7 + 2 'read word op_rdlong long %0000000_0 << 24 + @_rdlong << 7 + 2 'read long op_wrbyte long %0000000_0 << 24 + @_wrbyte << 7 + 2 'write byte op_wrword long %0000000_0 << 24 + @_wrword << 7 + 2 'write word op_wrlong long %0000000_0 << 24 + @_wrlong << 7 + 2 'write long op_asbyte long %0000000_0 << 24 + @_asbyte << 7 + 5 'assign byte op_asword long %0000000_0 << 24 + @_asword << 7 + 6 'assign word op_aslong long %0000000_0 << 24 + @_aslong << 7 + 4 'assign long op_rdbyteb long %0000000_0 << 24 + @_rdbyteb << 7 + 3 'read byte bitfield op_rdwordb long %0000000_0 << 24 + @_rdwordb << 7 + 3 'read word bitfield op_rdlongb long %0000000_0 << 24 + @_rdlongb << 7 + 3 'read long bitfield op_wrbyteb long %0000000_0 << 24 + @_wrbyteb << 7 + 7 'write byte bitfield op_wrwordb long %0000000_0 << 24 + @_wrwordb << 7 + 8 'write word bitfield op_wrlongb long %0000000_0 << 24 + @_wrlongb << 7 + 6 'write long bitfield op_asbyteb long %0000001_0 << 24 + @_wrbyteb << 7 + 7 'assign byte bitfield op_aswordb long %0000001_0 << 24 + @_wrwordb << 7 + 8 'assign word bitfield op_aslongb long %0000001_0 << 24 + @_wrlongb << 7 + 6 'assign long bitfield ' memory offsets and indexing (9) op_vbase long %0000000_0 << 24 + @_vbaseo << 7 + 2 'vbase -$80..+$7F op_vbase0 long %0000000_1 << 24 + @_vbaseo << 7 + 2 'vbase $0xxxx op_vbase1 long %0000001_1 << 24 + @_vbaseo << 7 + 2 'vbase $1xxxx op_pbase long %0000000_0 << 24 + @_pbaseo << 7 + 2 'pbase -$80..+$7F op_pbase0 long %0000000_1 << 24 + @_pbaseo << 7 + 2 'pbase $0xxxx op_pbase1 long %0000001_1 << 24 + @_pbaseo << 7 + 2 'pbase $1xxxx op_ibyte long %0000000_0 << 24 + @_index << 7 + 4 'byte index op_iword long %0000001_0 << 24 + @_index << 7 + 4 'word index op_ilong long %0000010_0 << 24 + @_index << 7 + 4 'long index ' branches jmp/jz/jnz/tjz/djnz (15) op_jmp long %0000000_0 << 24 + @_jmp << 7 + 1 'jmp -$80..+$7F op_jmp0 long %0000000_1 << 24 + @_jmp << 7 + 1 'jmp +$0xxxx op_jmp1 long %0000001_1 << 24 + @_jmp << 7 + 1 'jmp +$1xxxx op_jz long %0000000_0 << 24 + @_jz << 7 + 2 'jz -$80..+$7F op_jz0 long %0000000_1 << 24 + @_jz << 7 + 2 'jz +$0xxxx op_jz1 long %0000001_1 << 24 + @_jz << 7 + 2 'jz +$1xxxx op_jnz long %0000000_0 << 24 + @_jnz << 7 + 2 'jnz -$80..+$7F op_jnz0 long %0000000_1 << 24 + @_jnz << 7 + 2 'jnz +$0xxxx op_jnz1 long %0000001_1 << 24 + @_jnz << 7 + 2 'jnz +$1xxxx op_tjz long %0000000_0 << 24 + @_tjz << 7 + 3 'tjz -$80..+$7F op_tjz0 long %0000000_1 << 24 + @_tjz << 7 + 3 'tjz +$0xxxx op_tjz1 long %0000001_1 << 24 + @_tjz << 7 + 3 'tjz +$1xxxx op_djnz long %0000000_0 << 24 + @_djnz << 7 + 4 'djnz -$80..+$7F op_djnz0 long %0000000_1 << 24 + @_djnz << 7 + 4 'djnz +$0xxxx op_djnz1 long %0000001_1 << 24 + @_djnz << 7 + 4 'djnz +$1xxxx ' anchor drops (4) op_drop long %0000010_0 << 24 + @_drop << 7 + 6 'drop, sub op_dropp long %0000011_0 << 24 + @_drop << 7 + 6 'drop, sub w/push op_dropt long %0000000_0 << 24 + @_drop << 7 + 6 'drop, \sub op_droptp long %0000001_0 << 24 + @_drop << 7 + 6 'drop, \sub w/push ' calls (7) op_callos long %0000000_0 << 24 + @_callobj << 7 + 6 'call obj.sub op_callosi long %0000000_1 << 24 + @_callobj << 7 + 6 'call obj.sub[] op_callois long %0000001_0 << 24 + @_callobj << 7 + 6 'call obj[].sub op_calloisi long %0000001_1 << 24 + @_callobj << 7 + 6 'call obj[].sub[] op_calls long %0000000_0 << 24 + @_callsub << 7 + 3 'call sub op_callsi long %0000000_1 << 24 + @_callsub << 7 + 3 'call sub[] op_callp long %0000000_0 << 24 + @_callptr << 7 + 10 'call ptr ' returns (4) op_return long %0000000_0 << 24 + @_return << 7 + 13 'RETURN op_returnv long %0000001_0 << 24 + @_return << 7 + 13 'RETURN value op_abort long %0000000_1 << 24 + @_return << 7 + 13 'ABORT op_abortv long %0000001_1 << 24 + @_return << 7 + 13 'ABORT value ' call pointer generation (5) op_basesub long %0000000_0 << 24 + @_basesub << 7 + 5 'vbase/pbase of current op_baseobj long %0000000_0 << 24 + @_baseobj << 7 + 16 'vbase/pbase of obj op_baseobji long %0000000_1 << 24 + @_baseobj << 7 + 16 'vbase/pbase of obj[] op_subptr long %0000000_0 << 24 + @_subptr << 7 + 11 'base.sub op_subptri long %0000000_1 << 24 + @_subptr << 7 + 11 'base.sub[] ' case (7) op_casev long %0000000_0 << 24 + @_casev << 7 + 5 'case value, -$80..+$7F branch op_casev0 long %0000000_1 << 24 + @_casev << 7 + 5 'case value, +$0xxxx branch op_casev1 long %0000001_1 << 24 + @_casev << 7 + 5 'case value, +$1xxxx branch op_caser long %0000000_0 << 24 + @_caser << 7 + 10 'case range, -$80..+$7F branch op_caser0 long %0000000_1 << 24 + @_caser << 7 + 10 'case range, +$0xxxx branch op_caser1 long %0000001_1 << 24 + @_caser << 7 + 10 'case range, +$1xxxx branch op_casedone long %0000000_0 << 24 + @_casedone << 7 + 2 'case done ' lookup/lookdown (5) op_lookupv long %0000000_0 << 24 + @_lookupv << 7 + 9 'lookup value op_lookupr long %0000000_0 << 24 + @_lookupr << 7 + 14 'lookup range op_lookdnv long %0000000_0 << 24 + @_lookdnv << 7 + 9 'lookdown value op_lookdnr long %0000000_0 << 24 + @_lookdnr << 7 + 15 'lookdown range op_lookdone long %0000000_0 << 24 + @_lookdone << 7 + 1 'lookup/lookdown done ' miscellaneous (25) +231 op_acc long %0000000_0 << 24 + @_acc << 7 + 4 'acc (CLRACCA/CLRACCB/CLRACCS/FITACCA/FITACCB/FITACCS) op_ctr long %0000000_0 << 24 + @_gen << 7 + 4 'ctr (SYNCTRA/CAPCTRA/SYNCTRB/CAPCTRB) op_pol long %0000000_0 << 24 + @_gen << 7 + 6 'pol (POLVID/POLCTRA/POLCTRB) op_get long %0000000_0 << 24 + @_get << 7 + 5 'get (COGID/GETCNT/GETLFSR/GETPHSA/GETPHZA/GETCOSA/GETSINA/GETPHSB/GETPHZB/GETCOSB/GETSINB) op_set1 long %0000000_1 << 24 + @_gen << 7 + 4 'set1 (CLKSET/COGSTOP/LOCKRET/LOCKSET/LOCKCLR/PASSCNT/SNDSER/NOP/SETPIXx/SETF/SETTASK/CFGDACx/SETDACx/CFGDACS/SETDACS/SETPORx..) op_set1r long %0000000_1 << 24 + @_gen << 7 + 6 'set1p (LOCKSET/LOCKCLR/SNDSER w/push, CMPCNT) op_set2 long %0000000_0 << 24 + @_set2 << 7 + 6 'set2 (SETACCA/SETACCB/MACA/MACB/CFGPINS/WAITVID/WAITCNT/WAITPEQ/WAITPNE) op_set2r long %0000000_0 << 24 + @_set2r << 7 + 5 'set2r (MUL/MOVF/SCL/MIN/MAX/MOVS/MOVD/MOVI) op_coginit long %0000000_0 << 24 + @_coginit << 7 + 5 'COGINIT(cog,pgm,ptr) op_cognew long %0000000_1 << 24 + @_coginit << 7 + 5 'COGNEW(pgm,ptr) op_cognewp long %0000000_1 << 24 + @_coginit << 7 + 7 'COGNEW(pgm,ptr) w/push op_locknew long %0000000_0 << 24 + @_locknew << 7 + 2 'LOCKNEW op_rcvser long %0000000_0 << 24 + @_rcvser << 7 + 3 'RCVSER(var) op_rcvserp long %0000000_1 << 24 + @_rcvser << 7 + 3 'RCVSER(var) w/push op_getcnth long %0000000_0 << 24 + @_getcnth << 7 + 2 'GETCNTH op_jmptask long %0000000_0 << 24 + @_jmptask << 7 + 5 'JMPTASK(addr,mask) op_callb long %0000000_0 << 24 + @_callb << 7 + 3 'CALLB(addr) op_strsize long %0000000_0 << 24 + @_strsize << 7 + 6 'STRSIZE(@str) op_strcomp long %0000000_0 << 24 + @_strcomp << 7 + 10 'STRCOMP(@stra,@strb) op_bytefill long %0000000_0 << 24 + @_xfill << 7 + 15 'BYTEFILL(value,to,count) op_wordfill long %0000001_0 << 24 + @_xfill << 7 + 15 'WORDFILL(value,to,count) op_longfill long %0000010_0 << 24 + @_xfill << 7 + 15 'LONGFILL(value,to,count) op_quadfill long %0000100_1 << 24 + @_xfill << 7 + 15 'QUADFILL(value,to,count) op_pushcopy long %0000000_0 << 24 + @_pushcopy << 7 + 2 'push copy op_pop long %0000000_0 << 24 + @_pop << 7 + 1 'pop ' ' '************ '* Snippets * '************ ' ' ' ' Variable modifiers ' org snippet 'repeat-var loop (assign) _rep popbr :w 'pop var popbr y 'pop step popbr b 'pop terminal popbr a 'pop initial popbr :v 'pop branch offset cmps b,a wc 'get reverse range into c sumc :w,y 'update var with step rcl x,#1 wz 'get c into nz if_z cmps b,:w wc 'if forward range, check if var > terminal if_nz cmps :w,b wc 'if reverse range, check if var < terminal if_nc subptrb :v 'if var within range, branch if_nc addspb #4 '..unpop offset/initial/terminal/step pushbr :w 'push new var res 1 '(jmpback) :v res 1 :w res 1 org snippet 'var~, clear and post-clear (assign) _clr popbr x 'clear, pop var (single pop also used by op_writep) _clrpost pushbr #0 'post-clear, push 0 (leave var on stack) org snippet 'var~~, set and post-set (assign) _set popbr x 'set, pop var _setpost pushbr hFFFFFFFF 'post-set, push -1 (leave var on stack) org snippet '++var/--var/var++/var-- (assign) _incdec popbr x 'pop var if_nz pushbr x 'if nz, push original var sumc x,#1 'inc or dec var by c pushbr x 'push new var org snippet 'INCMOD/DECMOD(var,value) (assign) _idmod setbc :i,#26 'set INCMOD/DECMOD popbr x 'pop var popbr y 'pop value :i incmod x,y wc 'INCMOD/DECMOD if_nz_and_nc pushbr #0 'push false if nz and nc if_nz_and_c pushbr hFFFFFFFF 'push true if nz and c pushbr x 'push new var ' ' ' Unary functions ' org snippet _notb popbr x wz 'NOT, boolean muxz x,hFFFFFFFF pushbr x org snippet _una movs :inst,x '!, bitwise not..SUBCNT(x) popbr x nop :inst long %000011_001_1_1111<<18 + x<<9 pushbr x org snippet _neg popbr x '-, negate neg x,x pushbr x org snippet _abs popbr x '||, absolute abs x,x pushbr x org snippet _enc popbr x '>|, encode (0..32) enc x,x pushbr x org snippet _sqrt popbr x 'SQRT(x) setsqrl x getsqrt x wc if_nc jmp #$-1 pushbr x org snippet _qlogexp popbr x 'QLOG(x), c=0 if_nc qlog x 'QEXP(x), c=1 if_c qexp x getqz x wc if_nc jmp #$-1 pushbr x org snippet _rnd popbr x 'RNDF(x), c=0 mov y,#%10111 'RNDR(x), c=1 if_c ror y,#1 if_c setb :rot,#26 reps #32,#2 min x,#1 test x,y wc :rot rcr x,#1 'rcr/rcl pushbr x org snippet _getpix popbr x 'GETPIX(x) nop #2 nop #2 getpix x pushbr x ' ' ' Binary functions ' org snippet _andorb popbr x wz 'AND, boolean (c=0) if_z_ne_c popbr x 'OR, boolean (c=1) if_z_eq_c popbr x wz muxnz x,hFFFFFFFF pushbr x org snippet _xorb popbr y wz 'XOR, boolean muxnz y,hFFFFFFFF popbr x wz muxnz x,hFFFFFFFF xor x,y pushbr x org snippet _bin shl x,#26 '&! (bitwise andnot) .. - (sub) or :inst,x popbr y popbr x :inst long %000000_001_0_1111<<18 + x<<9 + y pushbr x org snippet _sal popbr y '<~, shift arithmetic left popbr x rev x,#0 sar x,y rev x,#0 pushbr x org snippet _rev popbr y '><, reverse bits popbr x neg y,y rev x,y pushbr x org snippet _mul popbr y '*, multiply and return lower long popbr x setmula x setmulb y getmull x wc if_nc jmp #$-1 pushbr x org snippet _scl popbr y '**, multiply return upper long, unsigned popbr x setmulu x setmulb y getmulh x wc if_nc jmp #$-1 pushbr x org snippet _divx popbr y '/, divide and return quotient, z=1 popbr x '//, divide and return remainder, z=0 setdiva x setdivb y getdivq x wc if_nc jmp #$-1 if_nz getdivr x pushbr x org snippet _fra popbr y '*/, fraction popbr x setdivu #0 setdivu x setdivb y getdivq x wc if_nc jmp #$-1 pushbr x ' ' ' Equality tests ' org snippet _e popbr y '<>, test not equal, c=0 popbr a ' =, test equal, c=1 cmp a,y wz if_z_eq_c not x pushbr x org snippet _m if_nc popbr y ' <, test below, z=1, c=0 popbr x ' >, test above, z=1, c=1 if_c popbr y '=> / >=, test above or equal, z=0, c=0 cmps x,y wc '=< / <=, test below or equal, z=0, c=1 muxc x,hFFFFFFFF if_nz not x pushbr x ' ' ' Math terms ' org snippet _divh64 popbr y 'QUO64(al,ah,b), z=1, c=0 popbr x 'QUO64U(al,ah,b), z=1, c=1 popbr a 'REM64(al,ah,b), z=0, c=0 if_nc setdiva a 'REM64U(al,ah,b), z=0, c=1 if_c setdivu a setdiva x setdivb y getdivq x wc if_nc jmp #$-1 if_nz getdivr x wc pushbr x org snippet _sqrt64 popbr x 'SQRT64(l,h) setsqrh x popbr x setsqrl x getsqrt x wc if_nc jmp #$-1 pushbr x org snippet _negb popbr x wz 'NEGB(x,bool) popbr x negnz x,x pushbr x org snippet _muxb popbr y wz 'MUXB(x,y,bool) popbr y popbr x muxnz x,y pushbr x org snippet _ternary popbr y 'a ? x : y popbr x popbr a wz if_nz pushbr x if_z pushbr y ' ' ' Math procedures ' org snippet _mul64 popbr x 'MUL64(a,b : l,h), c=0 if_nc setmula x 'MUL64U(a,b : l,h), c=1 if_c setmulu x popbr x setmulb x getmulh x wc if_nc jmp #$-1 pushbr x getmull x pushbr x org snippet _div64 popbr y 'DIV64(al,ah,b : q,r), c=0 popbr x 'DIV64U(al,ah,b : q,r), c=1 popbr a if_nc setdiva a if_c setdivu a setdiva x setdivb y getdivr x wc if_nc jmp #$-1 pushbr x getdivq x wc pushbr x org snippet _qtrig if_z_and_c popbr x 'QSINCOS(r,t : x,y), z=1, c=0 if_z_and_c setqz x 'QROTATE(x,y,t : x,y), z=1, c=1 popbr y 'QARCTAN(x,y : r,t), z=0 popbr x if_z_and_nc qsincos y,x if_z_and_c qrotate x,y if_nz qarctan x,y getqz x wc if_nc jmp #$-1 if_z getqy x pushbr x getqx x pushbr x org snippet _getacca getacca x 'GETACCA(l,h) getacca y pushbr y pushbr x org snippet _getaccb getaccb x 'GETACCB(l,h) getaccb y pushbr y pushbr x ' ' ' Constants ' org snippet _con8 rdbytec x,ptrb++ 'con8p/con8n _conxn if_c not x 'conm1 _conxp pushbr x 'con0..con3 org snippet _conexp rdbytec x,ptrb++ 'conexp decod5 x wz, wc if_z sub x,#1 if_c not x pushbr x org snippet _con16 rdbytec x,ptrb++ 'con16p/con16n rdbytec y,ptrb++ shl x,#8 or x,y if_c not x pushbr x org snippet _con32 reps #4,#3 'con32 nop rdbytec y,ptrb++ shl x,#8 or x,y pushbr x ' ' ' Inter-memory moves ' org snippet _reg2reg call #premove 'reg2reg cmp x,y wc if_c add x,a repd a,#4 if_c add y,a movs :move,x movd :move,y :step long 1<<9 + 1 '(nop) nop :move mov 0,0 sumc :move,:step org snippet _reg2stk call #premove 'reg2stk movd :set,x repd a,#1 getspb b setspb y :set setindb #0 pushb indb++ setspb b org snippet _reg2loc call #premove 'reg2loc movd :set,x subr y,dbase repd a,#1 getspb b setspb y :set setindb #0 pushbr indb++ setspb b org snippet _reg2mem call #premove 'reg2mem movd :set,x repd a,#1 getptrb b setptrb y :set setindb #0 wrlong indb++,ptrb++ setptrb b org snippet _stk2reg call #premove 'stk2reg movd :set,y repd a,#1 getspb b setspb x :set setindb #0 popbr indb++ setspb b org snippet _stk2stk call #premove 'stk2stk cmp x,y wc if_c add x,a if_c add y,a repd a,#6 if_c xor :rd,#%10 if_c xor :wr,#%10 getspb b setspb x :rd popbr a getspb x setspb y :wr pushb a getspb y setspb b org snippet _stk2loc call #premove 'stk2loc repd a,#6 nop subr y,dbase getspb b setspb x popbr a getspb x setspb y pushbr a getspb y setspb b org snippet _stk2mem call #premove 'stk2mem repd a,#3 getspb b setspb x nop popbr x wrlong x,y add y,#4 setspb b org snippet _loc2reg call #premove 'loc2reg subr x,dbase movd :set,y repd a,#1 getspb b setspb x :set setindb #0 popb indb++ setspb b org snippet _loc2stk call #premove 'loc2stk repd a,#6 nop subr x,dbase getspb b setspb x popb a getspb x setspb y pushb a getspb y setspb b org snippet _loc2loc call #premove 'loc2loc cmp x,y wc if_c add x,a if_c add y,a subr x,dbase subr y,dbase repd a,#6 if_c xor :rd,#%10 if_c xor :wr,#%10 getspb b setspb x :rd popb a getspb x setspb y :wr pushbr a getspb y setspb b org snippet _loc2mem call #premove 'loc2mem repd a,#3 subr x,dbase getspb b setspb x :loop popb x wrlong x,y add y,#4 setspb b org snippet _mem2reg call #premove 'mem2reg movd :set,y repd a,#1 getptrb b setptrb x :set setindb #0 rdlongc indb++,ptrb++ setptrb b org snippet _mem2stk call #premove 'mem2stk getspb b setspb y repd a,#2 getptrb a setptrb x rdlongc x,ptrb++ pushb x setptrb a setspb b org snippet _mem2loc call #premove 'mem2loc subr y,dbase getspb b setspb y repd a,#2 getptrb a setptrb x rdlongc x,ptrb++ pushbr x setptrb a setspb b org snippet _xmove mov b,x 'mem2mem/bytemove/wordmove/longmove decod5 b or :shl,x shl x,#26 or :rd,x or :wr,x call #premove cmp x,y wc repd a,#4 :shl if_c shl a,#0 if_c add x,a if_c add y,a :rd rdbytec a,x :wr wrbyte a,y sumc x,b sumc y,b org snippet _qmove call #premove 'quadmove cmp x,y wc repd a,#4 if_c shl a,#4 if_c add x,a if_c add y,a rdquad x wrquad y sumc x,#16 sumc y,#16 ' ' ' Register read/write/assign ' org snippet _rdreg popbr a 'read register movd :rd,a nop nop :rd pushbr $000 org snippet _wrreg popbr addr 'write register popbr x call #writer org snippet _asreg popbr addr 'assign register movs :rd,addr mov assign_i,callwriter nop :rd mov x,$000 jmp #assign_long org snippet _rdregb call #bitfield 'read register bitfield popbr a movs :rd,a nop nop :rd mov x,$000 jmp #readb org snippet _wrregb call #bitfield 'write/assign register bitfield popbr addr movs :rd,addr mov writeb_i,callwriter nop :rd mov bval,$000 if_z jmp #writeb 'write, z=1 jmp #assignb_long 'assign, z=0 ' ' ' Stack read/write/assign ' org snippet _rdstk popbr a 'read stack getspb y setspb a popbr x setspb y pushbr x org snippet _wrstk popbr a 'write stack popbr x getspb y setspb a pushb x setspb y org snippet _asstk popbr a 'assign stack getspb y setspb a popbr x subspb #1 getspb addr setspb y mov assign_i,callwrites jmp #assign_long org snippet _rdstkb call #bitfield 'read stack bitfield popbr a getspb y setspb a popbr x setspb y jmp #readb org snippet _wrstkb call #bitfield 'write/assign stack bitfield popbr a getspb y add a,#1 setspb a popb bval getspb addr setspb y mov writeb_i,callwrites if_z jmp #writeb 'write, z=1 jmp #assignb_long 'assign, z=0 ' ' ' Local read/write/assign ' org snippet _rdloci popbr y 'read local, indexed _rdloc rdbytec x,ptrb++ 'read local if_c add x,y _rdlocx getspb y 'read local, fixed setspb dbase subspb x popb x setspb y pushbr x org snippet _wrloci popbr y 'write local, indexed _wrloc rdbytec x,ptrb++ 'write local if_c add x,y _wrlocx popbr a 'write local, fixed getspb y setspb dbase subspb x pushbr a setspb y org snippet _asloci popbr y 'assign local, indexed _asloc rdbytec x,ptrb++ 'assign local if_c add x,y _aslocx getspb y 'assign local, fixed setspb dbase subspb x popb x getspb addr setspb y mov assign_i,callwrites jmp #assign_long org snippet _rdlocb call #bitfield 'read local bitfield popbr x getspb y setspb dbase subspb x popb x setspb y jmp #readb org snippet _wrlocb call #bitfield 'write/assign local bitfield popbr x getspb y setspb dbase subspb x popb bval getspb addr setspb y mov writeb_i,callwrites if_z jmp #writeb 'write, z=1 jmp #assignb_long 'assign, z=0 ' ' ' Memory read/write/assign ' org snippet _rdbyte popbr x 'read byte rdbyte x,x pushbr x org snippet _rdword popbr x 'read word rdword x,x pushbr x org snippet _rdlong popbr x 'read long rdlong x,x pushbr x org snippet _wrbyte popbr a 'write byte popbr x wrbyte x,a org snippet _wrword popbr a 'write word popbr x wrword x,a org snippet _wrlong popbr a 'write long popbr x wrlong x,a org snippet _asbyte popbr addr 'assign byte rdbyte x,addr mov assign_i,:i mov mask,#$FF jmp #assign :i wrbyte x,addr org snippet _asword popbr addr 'assign word rdword x,addr mov assign_i,:i mov mask,:m jmp #assign :i wrword x,addr :m long $0000FFFF org snippet _aslong popbr addr 'assign long rdlong x,addr mov assign_i,:i jmp #assign_long :i wrlong x,addr org snippet _rdbyteb call #bitfield 'read byte bitfield popbr x rdbyte x,x jmp #readb org snippet _rdwordb call #bitfield 'read word bitfield popbr x rdword x,x jmp #readb org snippet _rdlongb call #bitfield 'read long bitfield popbr x rdlong x,x jmp #readb org snippet _wrbyteb call #bitfield 'write/assign byte bitfield popbr addr rdbyte bval,addr mov writeb_i,:wr if_z jmp #writeb 'write, z=1 mov mask,#$FF 'assign, z=0 jmp #assignb :wr wrbyte x,addr org snippet _wrwordb call #bitfield 'write/assign word bitfield popbr addr rdword bval,addr mov writeb_i,:wr if_z jmp #writeb 'write, z=1 mov mask,:mask 'assign, z=0 jmp #assignb :wr wrword x,addr :mask long $0000FFFF org snippet _wrlongb call #bitfield 'write/assign long bitfield popbr addr rdlong bval,addr mov writeb_i,:wr if_z jmp #writeb 'write, z=1 jmp #assignb_long 'assign, z=0 :wr wrlong x,addr ' ' ' Memory offsets and indexing ' org snippet _vbaseo call #xword add x,vbase pushbr x org snippet _pbaseo call #xword add x,pbase pushbr x org snippet _index popbr y 'ibyte, x=0 shl y,x 'iword, x=1 popbr x 'ilong, x=2 add x,y pushbr x ' ' ' Branch jmp/jz/jnz/tjz/djnz ' org snippet _jmp call #xword 'jmp addptrb x org snippet _jz popbr y wz 'jz call #xword if_z addptrb x org snippet _jnz popbr y wz 'jnz call #xword if_nz addptrb x org snippet _tjz popbr y wz 'tjz if_nz pushbr y call #xword if_z addptrb x org snippet _djnz popbr y 'djnz sub y,#1 wz if_nz pushbr y call #xword if_nz addptrb x ' ' ' Drop anchor ' ' \sub x = %00 ' \sub result x = %01 ' sub x = %10 ' sub result x = %11 ' org snippet _drop pushbr dcall 'push dcall (later used for pcurr) getspb dcall 'set new dcall pushbr dbase 'push return dbase pushbr vbase 'push return vbase or x,pbase 'push return pbase w/flags pushbr x pushbr #0 'init 'result' to 0 ' ' ' Call ' ' obj.sub z=1, c=0 ' obj.sub[] z=1, c=1 ' obj[].sub z=0, c=0 ' obj[].sub[] z=0, c=1 ' sub c=0 ' sub[] c=1 ' ptr ' org snippet _callobj rdbytec x,ptrb++ 'get obj byte if_nz popbr a 'add any obj index if_nz add x,a rdbytec y,ptrb++ 'get sub byte if_c popbr a 'add any sub index if_c add y,a jmp #call_obj 'jump to handler org snippet _callsub rdbytec y,ptrb++ 'get sub byte if_c popbr a 'add any index if_c add y,a jmp #call_sub 'jump to handler org snippet _callptr popbr x 'get sub [31..29]/[15..13], vbase [28..16], pbase [12..0] mov vbase,#0 'clear vbase/pbase mov pbase,#0 mov y,x 'get sub (6-bits) mov a,x shr y,#32-3-3 and y,#%111000 shr a,#16-3 and a,#%000111 or y,a jmp #call_ptr 'jump to handler ' ' ' RETURN/ABORT ' ' RETURN z=1, c=0 ' ABORT z=1, c=1 ' RETURN value z=0, c=0 ' ABORT value z=0, c=1 ' org snippet _return if_z setspb dbase 'if no value, return result if_z popb x if_nz popbr x 'if value, pop it :again setspb dbase 'set dbase popbr pbase 'pop pbase popbr vbase 'pop vbase popbr dbase 'pop dbase popbr y 'pop pcurr if_c test pbase,#%10 wc 'if abort and try, return again if_c jmp #:again setptrb y 'set ptrb to pcurr test pbase,#%01 wc 'push result? andn pbase,#%11 if_c pushbr x ' ' ' Get vbase/pbase ' org snippet _basesub mov y,vbase shl y,#16-4 mov x,pbase shr x,#4 or x,y pushbr x ' ' ' Get pbase/vbase of obj ' ' obj c=0 ' obj[] c=1 ' org snippet _baseobj rdbytec x,ptrb++ 'get obj byte if_c popbr y 'handle index if_c add x,y shl x,#2 'scale offset add x,pbase 'get obj pbase/vbase offset rdlong x,x mov y,x 'get obj vbase shr y,#16-4 add y,vbase and y,h0001FFF0 shl y,#16-4 shl x,#4 'get obj pbase add x,pbase and x,h0001FFF0 shr x,#4 or x,y 'merge vbase/pbase pushbr x 'push obj vbase/pbase ' ' ' Make subroutine ptr ' ' base.sub c=0 ' base.sub[] c=1 ' org snippet _subptr rdbytec a,ptrb++ 'get sub number if_c popbr b 'handle sub index if_c add a,b mov b,a 'split 6-bit index and b,#%111000 'top 3 bits into [31..29] shl b,#32-6 and a,#%000111 'bottom 3 bits into [15..13] shl a,#16-3 popbr x 'pop base vbase/pbase or x,b 'install index into pbase/vbase or x,a pushbr x 'push subroutine pointer ' ' ' Case ' org snippet 'case value _casev call #xword 'get branch address popbr a 'pop value popbr y 'pop target cmp a,y wz 'value = target? if_nz subspb #1 'if mismatch, unpop target if_z addptrb x 'if match, branch org snippet 'case range (z=1) _caser call #xword 'get branch address popbr b 'pop range end popbr a 'pop range begin popbr y 'pop target sub b,a '||(end - begin), c=1 if reverse range abs b,b wc sub y,a '+/-(target - begin) negc y,y cmp b,y wc 'match if ||(end - begin) => +/-(target - begin) if_c subspb #1 'if mismatch, unpop target if_nc addptrb x 'if match, branch org snippet 'case done _casedone popbr x 'pop target popbr x 'pop address setptrb x 'jump to address ' ' ' Lookup/lookdown ' org snippet 'lookup value _lookupv popbr a 'pop value popbr x 'pop index popbr y 'pop target cmp x,y wz 'match if index = target if_nz subspb #1 'if no match, unpop target if_nz add x,#1 '..increment index if_nz pushbr x '..push index if_z popbr addr 'if match, pop address if_z pushbr a '..push result if_z setptrb addr '..branch org snippet 'lookup range _lookupr popbr b 'pop range end popbr a 'pop range begin popbr x 'pop index popbr y 'pop target sub b,a 'end - begin abs b,b wc '||(end - begin), c=1 if reverse range sub y,x 'target - index sumc a,y 'result = begin +/- (target - index) cmp b,y wc 'match if ||(end - begin) => (target - index) if_c subspb #1 'if no match, unpop target if_c addx x,b '..add ||(end - begin) + 1 to index if_c pushbr x '..push index if_nc popbr addr 'if match, pop address if_nc pushbr a '..push result if_nc setptrb addr '..branch org snippet 'lookdown value _lookdnv popbr a 'pop value popbr x 'pop index popbr y 'pop target cmp a,y wz 'match if value = target if_nz subspb #1 'if no match, unpop target if_nz add x,#1 '..increment index if_nz pushbr x '..push index if_z popbr addr 'if match, pop address if_z pushbr x '..push result if_z setptrb addr '..branch org snippet 'lookdown range _lookdnr popbr b 'pop range end popbr a 'pop range begin popbr x 'pop index popbr y 'pop target sub b,a '||(end - begin), c=1 if reverse range abs b,b wc sub y,a '+/-(target - begin) negc y,y cmp b,y wc 'match if ||(end - begin) => +/-(target - begin) if_c subspb #1 'if no match, unpop target if_c addx x,b '..add ||(end - begin) + 1 to index if_c pushbr x '..push index if_nc popbr addr 'if match, pop address if_nc add x,y '..result = index +/- (target - begin) if_nc pushbr x '..push result if_nc setptrb addr '..branch org snippet 'lookup/lookdown done _lookdone addspb #3 'pop index/target/address pushbr #0 'push 0 result ' ' ' Miscellaneous ' org snippet _acc rdbyte a,ptrb++ 'acc movd :inst,a nop nop :inst long %000011_000_1_1111_000000000_000001000 org snippet _gen rdbyte a,ptrb++ 'gen movs :inst,a if_c popbr x nop :inst long %000011_010_1_1111<<18 + x<<9 if_nc pushbr #0 if_c pushbr hFFFFFFFF org snippet _get rdbyte a,ptrb++ 'get movs :inst,a nop nop :inst long %000011_001_1_1111<<18 + x<<9 pushbr x org snippet _set2 rdbyte a,ptrb++ 'set2 shl a,#24 sar a,#1 or :inst,a popbr y popbr x :inst long %000000_000_0_1111<<18 + x<<9 + y org snippet _set2r rdbyte a,ptrb++ 'set2r movi :inst,a popbr y popbr x :inst long %000000_000_0_1111<<18 + x<<9 + y pushbr x org snippet _coginit popbr y 'COGINIT(cog,pgm,par) / COGNEW(pgm,par) popbr x if_nc popbr a if_c mov a,#8 setcog a coginit x,y wc,wr if_c mov x,hFFFFFFFF pushbr x org snippet _locknew locknew x wc 'LOCKNEW if_c mov x,hFFFFFFFF pushbr x org snippet _rcvser rcvser x wc 'RCVSER(var) if_c muxc y,hFFFFFFFF if_c pushbr y pushbr x org snippet _getcnth getcnt x 'GETCNTH getcnt x pushbr x org snippet _jmptask popbr y 'JMPTASK(addr,mask) and y,#%1111 or :inst,y popbr x nop :inst jmptask x,#0 org snippet _callb popbr x 'CALLB(addr) subspb #1 callb x addspb #1 org snippet _strsize popbr x 'STRSIZE(@str) mov y,x :loop rdbytec a,x wz if_nz add x,#1 if_nz jmp #:loop sub x,y pushbr x org snippet _strcomp popbr y 'STRCOMP(@stra,@strb) popbr x :loop rdbytec a,x rdbyte b,y add x,#1 add y,#1 cmp a,b wz if_z tjz a,#:got if_z jmp #:loop :got muxz x,hFFFFFFFF pushbr x org snippet _xfill shl x,#26 'BYTEFILL/WORDFILL/LONGFILL/QUADFILL(value,addr,count) or :wr,x shr x,#26 decod5 x popbr y wz popbr a popbr b setquad #_xfill reps #4,#1 setindb #_xfill mov indb++,b :wr if_nz_and_nc wrbyte b,a if_nz_and_c wrquad a if_nz add a,x if_nz djnz y,#:wr setquad #$1FF org snippet _pushcopy popbr x 'push copy pushbr x pushbr x org snippet _pop rdbytec x,ptrb++ 'pop addspb x
Nice work.
I only took a quick glance so far, looks like plenty to chew over
'org snippet' looks interesting...
Making it cache them would be really easy - have a long for each snippet, the bottom 9 bits holding the snippet's cogram address or 0 if it's not loaded, and the rest holding when this snippet was last used. Whenever a new snippet is needed and there isn't enough room for it, the oldest one would be unloaded and replaced with the new one.
Obviously, caching them cuts down on the amount of user PASM space you have, but maybe you could specify how much space you needed and it would make the cache size fill only the remainder?
Mike's right. The hassle of determining whether or not what you wanted was already cached would take longer than just loading what are often 4-long snippets (using RDLONGC, which caches). This is where some kind of content-addressable memory subsystem would help a lot.
Are you planning to document the instruction set used by the Spin interpreter so that it could be used as a target for other languages?
Thanks,
David
I haven't thought about it. Do you think documentation would be necessary for someone to use the code? I figure they'd probably modify it in some ways after figuring out how it works. The resident portion of the interpreter spans from $147 to $1F5, with some of that being variable space, so it's not that much to learn. Some instruction snippets push stuff and other instructions pop stuff, and some do both. It's RAM-based, so it's nothing that would have to be adhered to strictly.
I was thinking that the bytecodes could be made dynamic at compile time, so that only those used need be included in the interpreter. That would shorten the descriptor table and eliminate unused snippets. Right now the whole interpreter is just over 5KB. Most programs would use way less than that. The advantage of keeping the whole interpreter resident leaves the door open for dynamic overlays.
Sure, most of the Spin2 interpreter is hub RAM based and there's some potential benefit in trimming down the instruction set on an application by application basis, but then there's no run-time commonality from application to application ... not a bad thing if you want to think of the interpreter like a run-time library with the Spin2 compiler managing the packaging of the interpreter with the program and the assumption at run-time that there will always be a native interpreter or other "main" native program that will be given control initially. This is opposite of what Spin has assumed on the Prop1, that there's a built-in Spin machine that gets control initially and can surrender control to one of the native processors.
I think the idea of multiple interpreters (for Spin2 or C or whatever) is fine, but the conceptual base then needs to shift to the native instruction set as the primary or initial program to be given control when booted.
I think that as time goes by and the interpreter is improved, there will be many changes to it, making it a moving target for documentation. I think what you said about shifting focus to the native instruction set is the way to go. Spin2 is writ in water.
With the bigger address space, SPIN2 can become a general purpose programming language.
Someone will figure out how to make it interact easily with OS resources and GUI window managers like TK.
Chip, you may as well face it, and accept it now so SPIN2 can grow ....
Good question ;-)
I would like to see better docs so that it could become part of a library to support other languages too. IOW I would like to see a BASIC frontend, and I would like to see a variant of Spin where indentation is not forced (ie using endif, endrepeat/endloop, endcase, or the horrid "{" and "}" from C etc.). Switching between the forced indentation and others should even be possible on the fly by extending the editor. Don't get me wrong, I love the enforced identation, but so many don't like it I think it would be great to provide an alternative.
The overhead of testing to see whether small "snippets" are already loaded is a large percentage of the load time, so as others have said, is detrimental to the timing and a waste of code space.
By being "soft", improvements can be made. Great times ahead.
Thanks again Chip, well done!
Fascinating... I suppose I always envisioned that wafer being exposed all at once by a single great big mask for each step. I am amazed at the multiple disciplines you had to master to make these chips real. I might have thought to design my own CPU and maybe give it cores but the PLL's and counter-timers are genius totally out of my pay grade and the translation to IC mask HEAD EXPLODES.
If you did wafer probe then you could burn fuses to uniquely id each die during test, and keep a record of their locations. Some companies do this. (I could see the military wanting tracking like that, but have no experience with miltary electronics.) But it doesn't always make sense to do wafer probe even if you're setup to do it. Someone needs to run the numbers to see if it pays off. (e.g. not packaging bad parts.) For WLBGAs you typically test in wafer form (and hopefully several die in parallel), and then typically no testing is done after dicing the wafer. The chips that I've worked on lately are WLBGAs, and you can see the ATE pass/fail results mapped onto an image of the wafer.
How do you imagine this working:
1) Compile Spin to native code for your target, say x86, ARM etc? Like a C compiler does.
2) Compile Spin to the standard byte codes and have a native, x86, ARM.., program interpret them? Like a JVM.
One thing that worries me with Spin as a general purpose language is that I'm used to the tight and seamless integration of the high level Spin syntax with the low level PASM. So much so that in my mind that Spin is Spin + PASM. Which leads to:
3) Adopt the instruction set of the target machine for the PASM parts? Results in non-portable hence non-general purpose use.
4) Keep the to PASM instruction set? Requires a Prop simulator on the target.
Interesting idea...
Of course, no one will do that...
The other option is to make certain that when the wafer is cut, that the individual chips are always picked in the same order, and that they use a new tray for each wafer.
(This assumes that when the chips are packaged, they're always picked in a predictable order off the tray, and also placed in a known order on the output tray)
The problem is that it'll probably cost too much in man-hours to plan and verify this to make it worthwhile.
(Unless there's one or more chips with the same faults from every wafer)