[puzzle] Quine in Spin
Andrey Demenev
Posts: 386
Attached simple quine program in Spin, written in 30 minutes, writes its own source code to serial port. Can someone come up with a less rude approach? Assembly anyone?

Comments
CON _CLKMODE = XTAL1 + PLL16X _XINFREQ = 5_000_000 OBJ serial: "FullDuplexSerial" DAT str file "quine.spin" byte 0 PUB main serial.start(31, 30, 0, 115200) serial.str(@str)If quine.spin is non-ascii you may have a 0 byte in there...probably safer to name your terminating byte, and do a repeat and serial.tx() each byte individually.
Jonathan
-Phil
Not only that it is not even a good cheat as the result is wrong.
By using file you are effectively including a copy of the source within the source. But the printed output ony prints the top level copy not the included copy.
I think Phil has a good point that a Spin file that includes an external object really shouldn't be allowed. Here is a version that doesn't include an object.
DAT t file "q.spin" byte 0 PUB main str(@t) PUB str(ptr) repeat while byte[ptr] tx(byte[ptr++]) PUB tx(val) long[$12340004] := val word[$12340000] := 1 repeat while word[$12340000]And for those that think that the "file" directive is cheating, here's a version without the file directive.
PUB main | ptr ptr := @table + 10 repeat 23 str(ptr) tx(13) ptr += strsize(ptr) + 1 ptr := @table repeat 25 str(@table) tx($22) str(ptr) tx($22) str(@table+6) tx(13) ptr += strsize(ptr) + 1 PUB str(ptr) repeat while byte[ptr] tx(byte[ptr++]) PUB tx(val) long[$12340004] := val word[$12340000] := 1 repeat while word[$12340000] dat table byte "byte ", 0 byte ", 0", 0 byte "PUB main | ptr", 0 byte " ptr := @table + 10", 0 byte " repeat 23", 0 byte " str(ptr)", 0 byte " tx(13)", 0 byte " ptr += strsize(ptr) + 1", 0 byte " ptr := @table", 0 byte " repeat 25", 0 byte " str(@table)", 0 byte " tx($22)", 0 byte " str(ptr)", 0 byte " tx($22)", 0 byte " str(@table+6)", 0 byte " tx(13)", 0 byte " ptr += strsize(ptr) + 1", 0 byte "PUB str(ptr)", 0 byte " repeat while byte[ptr]", 0 byte " tx(byte[ptr++])", 0 byte "PUB tx(val)", 0 byte " long[$12340004] := val", 0 byte " word[$12340000] := 1", 0 byte " repeat while word[$12340000]", 0 byte "dat table", 0Yes it's the same idea as yours, but it doesn't include an external object, and it is less source code.
Dave
I was thinking exactly the same. The file directive is a feature of the Spin compiler, which includes the file at compile time into the binary.
So here is a solution in binary form that needs no objects and runs on a real Propeller. The serial output at P30 has a baudrate of 9600 Baud.
You have 3 seconds after starting the binary to receive the output in a Terminal (i.e. PST). You get the source in the Terminal and you can compile this source again to a binary.
Andy
Can you explain what your tx method is doing?
Andy,
I disagree. The file directive is a cheat, because it pulls in data from somewhere else.
-Phil
After reading this short discussion, I'm curious about characterizing file:
There is the loading of the program.
Then there is the running of the program.
And the output of the program.
If one were to type the program into the Propeller, that's not considered against the rules.
So, what if one were to use a software system to pull the program listing from a disk?
Both of those are loading the program, right?
How does file: differ?
When the program is run, all the program that is loaded is reproduced at the output right? Do the characters file: appear in the output?
If not, how does that differ from automatically loading a program, in the way I described above?
I've never heard of a quine before, but the simple definition is a program that prints out it's own source code. A Spin program that uses the file directive to include it's own source meets that criteria. "file" works because it's including itself. It wouldn't work if the file directive was including another file, because that other file wouldn't exist.
I think the test for whether a quine is valid is to comple it, run it and then compare the output to the source. The quine must be self-contained, meaning that it can't use an input stream to create its output.
Dave
1. The program has to run natively on the Prop.
2. The program source cannot refer to external resources (i.e. no OBJ and no file directives).
-Phil
I guess if you do 300 bps baud then spin could do serial easily though...
File just embeds characters, you can do that without the file command...
Then, it seems you just have to output almost the same thing twice...
BTW, I added a serial decoder to SpinSim to run Ariba's binary file. I'll include that in the next update.
' Copy this into the PropTool, save it as 'quine.spin' and press F10 CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 PUB main : p | t,txd outa[30]~~ ' set idle state dira[30]~~ ' make tx pin an output waitcnt(clkfreq*3+cnt) repeat while (txd := data[p++]) ' read data txd := ((txd | $300) << 2) ' add start and stop bits t := cnt ' sync repeat 11 ' start + eight data bits + 2stop waitcnt(t += clkfreq/9600) ' wait bit time outa[30] := (txd >>= 1) ' output bit DAT data file "quine.spin" byte 0That sounds backwards to me. Using the list command should not count as a quine, while including the source as data is the whole point of quining.
Properties:
Disclaimer
Trying to read and understand the source can lead to madness. Do not blame me if your mind is affected.You have been warned.
I think the code below meets the requirements posted by Phil.
CON _CLKMODE = XTAL1 + PLL16X _CLKFREQ = 80_000_000 Baud = 38400 'Tested up to 250000 baud TX = 30 {{ According to Wikipedia, a 'quine' is a computer program which produces a copy of its own source code as its only output. [B]Thus far the solutions that I have seen have produced 'human readable' source code, but isn't 'machine code' just as much valid source code as what you could read in PASM?[/B] This program launches a PASM routine to serially transmit the upper 32768 bytes of the Propeller at a desired baud and pin. The output is open-collector / inverted driven / with 1 start bit and no stop bit. At 38400 baud it takes about 8.5 seconds to transmit the data. _ (10/Baud)* 32768 = 8.53 seconds }} PUB QUINE cognew(@PASM, 0) DAT PASM org andn outa, PinMask 'PreSet pin LOW andn dira, PinMask 'Make min an Input mov Index, #0 '<-- Starting Address ; Clear/Reset Index NextData rdbyte Buff, Index 'Read BYTE add Index, #1 'Increment BYTE Index cmp Index, SizeInBytes wz 'set Z flag if the last BYTE address has been reached add Buff, #$100 'Add Start Bit ; LOGIC "1" shl Buff, #1 'Add Stop Bit ; LOGIC "0" mov BitCount, #10 'Start Bit + Stop Bit + Byte = 10 bits total Bits ror Buff, #1 wc 'Send Bits LSB first muxnc dira, PinMask 'Set Data Bit (Inverted data) mov delay, BaudDelay 'Pause based on Baud djnz delay, #$ 'delay djnz BitCount, #Bits 'More Bits to send? if_nz jmp #NextData 'Send Next Data Byte cogid temp 'Shut down cog cogstop temp temp PinMask long |<TX BaudDelay long ((_CLKFREQ / Baud)>>2)-5 delay long 0 Index long 0 Buff long 0 BitCount long 0 SizeInBytes long $8000To be precise, that code itself is not a quine, although its output can be considered a quine.
Why wouldn't it be? ... if you saved the file as a EEPROM File, the output generated is a mirror copy <--- with the exception of the Stack space area during run-time, but the program itself (the source code) would be intact. Quine doesn't necessarily imply that it's human readable, just that it's valid source code of itself.
Now this is odd because the resulting quine would be source code but basically obfuscated and not very human readable. More weird is the fact that the finished quine was written by the original asm program, not the author himself.
Because it outputs something different from the original code.
I think one of most precise definitions of a quine programs is the following:
For Spin source code program, lets call it x1, f(x) is a combination of spin compiler and the chip ("underlaying system"), lets call it f1(x) . For compiled program, lets call it x2, f(x) is bare chip, lets call it f2(x). Obviously, x1 != x2, f1(x1) != x1, and f2(x2) == x2.
Does that matter at all? I do not think so. Is it a program? Yes. Does it produce an exact copy of its own source code? Who cares how it was generated?
Once compiled and written to the EEPROM of the first Propeller, it reproduces itself on the next Propeller. The schematic shows 3 Propeller chips connected in a chain, but the number is not limited.
OBJ loader : "PropellerLoader" PUB LoadPropeller dira[16] := 1 'LED on outa[16] := 1 waitcnt(clkfreq + cnt) 'wait a second 'reproduce code on next Propeller: loader.Connect(0, 1, 2, 1, loader#LoadRun, 0)This is not tested. For a possible test you can connect a LED on P16 of every Propeller.Not sure if this is still the idea of a quine
Andy