Shop OBEX P1 Docs P2 Docs Learn Events
[puzzle] Quine in Spin — Parallax Forums

[puzzle] Quine in Spin

Andrey DemenevAndrey Demenev Posts: 377
edited 2011-03-22 12:39 in Propeller 1
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?
«1

Comments

  • RaymanRayman Posts: 14,877
    edited 2011-03-18 06:47
    Interesting... Can a Spin file be empty? That would make the smallest quine program :)
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-03-18 07:08
    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)
    
  • lonesocklonesock Posts: 917
    edited 2011-03-18 07:29
    Nice, Dave!

    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
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-03-18 08:10
    Here's a shorter version that runs under SpinSim. I named it q.spin to shorten the name. If I run "spinsim q.binary >x" under Windows the file x is an exact copy of q.spin. q.spin is an 8-bit ASCII file, so it won't have any zeros in it.
    OBJ s:"conio"
    DAT t file "q.spin"
    byte 0
    PUB main
    s.str(@t)
    
  • Heater.Heater. Posts: 21,230
    edited 2011-03-18 09:03
    I don't think printing your own source from a file satisfies the requiremeny of the quine challenge.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-18 09:12
    I'm not even sure that using external objects should be allowed.

    -Phil
  • Andrey DemenevAndrey Demenev Posts: 377
    edited 2011-03-18 09:21
    using "file" directive is a cheat, I think, since the compiled program has access to original source.
  • Heater.Heater. Posts: 21,230
    edited 2011-03-18 09:58
    Using the file directive is cheating.
    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.
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-03-18 11:00
    I don't think the file statement violates the quine rules. It basically includes itself, which means it is self-contained. I could give anybody the binary file, and they could run it to generate the source, and they could regenerate the binary. So it is completely self-contained. This is different than a C file that uses a #include to include source code that would print out the main file. You could not regenerate the complete source file from the binary.

    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", 0
    
  • Andrey DemenevAndrey Demenev Posts: 377
    edited 2011-03-18 11:10
    Dave Hein wrote: »
    And for those that think that the "file" directive is cheating, here's a version without the file directive.
    It is the same as in first post, with hex encoded data replaced with plain text
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-03-18 11:22
    Andrey,

    Yes it's the same idea as yours, but it doesn't include an external object, and it is less source code.

    Dave
  • AribaAriba Posts: 2,690
    edited 2011-03-18 12:06
    Dave Hein wrote: »
    I don't think the file statement violates the quine rules. It basically includes itself, which means it is self-contained. I could give anybody the binary file, and they could run it to generate the source, and they could regenerate the binary. So it is completely self-contained. This is different than a C file that uses a #include to include source code that would print out the main file. You could not regenerate the complete source file from the binary.

    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
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-18 12:15
    Dave,

    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
  • potatoheadpotatohead Posts: 10,261
    edited 2011-03-18 12:32
    What fun!

    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?
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-03-18 12:56
    Dave,

    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.
    SpinSim uses the locations at $1234000X for console and file I/O. word[$12340000] is the system I/O command and long[$12340004] is a parameter used by the command. A character is sent to the console by storing the character at the parameter location and setting the command to 1. The command is completed when word[$12340000] becomes zero.

    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
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-18 13:02
    Dave Hein wrote:
    A Spin program that uses the file directive to include it's own source meets that criteria.
    Well, let's change the criterion, then. A quine that uses file is not even interesting. I would propose the following constraints:

    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
  • RaymanRayman Posts: 14,877
    edited 2011-03-18 16:27
    Without file shouldn't be too tough, but without OBJ might be tough....
    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...
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-03-18 16:46
    Ariba's program contains a 9600 baud serial transmit routine. The output from his program is shown below. This could be combined with Andrey's method using hex or my method using ASCII to meet Phil's requirements.

    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 0
    
  • RaymanRayman Posts: 14,877
    edited 2011-03-18 17:14
    Ooops, I should have followed this thread closer...
  • davidsaundersdavidsaunders Posts: 1,559
    edited 2011-03-18 17:34
    My understanding of Quine is that you should not include the source as data in the program in any way. I do remember when I was first introduced to the concept, in interpreted BASIC (but that was in elementary school). I had written a program that in its code, in part, used a list command redirected to disk. My professor told me that this was a Quine.
  • mparkmpark Posts: 1,305
    edited 2011-03-18 18:35
    My understanding of Quine is that you should not include the source as data in the program in any way. I do remember when I was first introduced to the concept, in interpreted BASIC (but that was in elementary school). I had written a program that in its code, in part, used a list command redirected to disk. My professor told me that this was a Quine.

    That 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.
  • Andrey DemenevAndrey Demenev Posts: 377
    edited 2011-03-18 18:38
    Another one

    Properties:
    • assembler
    • no external source code
    • 5MHz crystal only
    • 115200 bit/s serial output
    • includes source of Spin loader stub in obscured form
    • only tested with BST compiler

    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.
  • Andrey DemenevAndrey Demenev Posts: 377
    edited 2011-03-18 18:46
    mpark wrote: »
    including the source as data is the whole point of quining.
    I personally find programs that do not contain their own code in some form more interesting, more educating, and more fun
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2011-03-18 22:49
    I hate these golf challenges!!! ... no really I don't it's great exercise for the brain.


    I think the code below meets the requirements posted by Phil.
    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).
    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      $8000
    
  • Andrey DemenevAndrey Demenev Posts: 377
    edited 2011-03-18 23:10
    I think the code below meets the requirements posted by Phil.

    To be precise, that code itself is not a quine, although its output can be considered a quine.
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2011-03-18 23:25
    Andrey Demenev,
    To be precise, that code itself is not 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.
  • Heater.Heater. Posts: 21,230
    edited 2011-03-18 23:32
    I have not looked at Andrey's assembler quine yet as I only have a phone to chat with today. But I'm thinking that an assembler language quine cold be written that essentially dis-assembles itself into a bunch of DAT LONG decalations with a little Spin header to get it started. That output could then be used as source to compile the same binary again.

    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.
  • Andrey DemenevAndrey Demenev Posts: 377
    edited 2011-03-18 23:53
    Why wouldn't it be?

    Because it outputs something different from the original code.

    I think one of most precise definitions of a quine programs is the following:
    fixed point (also known as an invariant point) of a function is a point that is mapped to itself by the function (f(x) = x). Quines are such fixed points, where:
    x is the sourcecode, and f() is the interpreter / compiler with its underlaying system.

    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.
    heater wrote:
    Now this is odd because the resulting quine would be source code but basically obfuscated and not very human readable.

    :) My program does actual dissembling, so you get assembly source, with conditions, instructions and modifiers, not just a bunch of LONGs. With little effort, it can even be made to preserve original labels and use them instead of hard-coded operands. Then one can even understand how it works :)
    heater wrote:
    More weird is the fact that the finished quine was written by the original asm program, not the author himself.

    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?
  • Heater.Heater. Posts: 21,230
    edited 2011-03-19 00:24
    All right, Andrey, you have taken it to the next level of cunningness. I might have guessed you would:) Shame I can't get to see it today.
  • AribaAriba Posts: 2,690
    edited 2011-03-19 02:40
    So if you don't need to reproduce the source code then here is a solution with absolutely no external resources involved (no terminal, no compiler, no harddisk, no PC).
    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 :smile:

    Andy
    attachment.php?attachmentid=79429&d=1300527593
    600 x 206 - 6K
Sign In or Register to comment.