Detailed Listing from OpenSpin
in Propeller 1
Like a number of people before me, I found myself in need of understanding where my Propeller code is located in memory. However, I seem to have taken a different approach to the problem. Instead of disassembling the generated binary, I have instrumented the OpenSpin compiler.
This has the disadvantage that it can only be applied to code assembled with OpenSpin.
Conversely, it enables the listing to refer directly to the source code. An abbreviated example output is given below.
My additions to OpenSpin can be found at: https://github.com/Memotech-Bill/OpenSpin
The code probably needs further testing, but it does everything that I want, and I can go back to trying to work out what is wrong with my Propeller code.
This has the disadvantage that it can only be applied to code assembled with OpenSpin.
Conversely, it enables the listing to refer directly to the source code. An abbreviated example output is given below.
My additions to OpenSpin can be found at: https://github.com/Memotech-Bill/OpenSpin
The code probably needs further testing, but it does everything that I want, and I can go back to trying to work out what is wrong with my Propeller code.
0000 03D09000 Frequency 64000000
0004 67 Clock Mode
0005 D9 Check Sum
0006 0010 Base of Program
0008 6D9C Base of Variables
000A 75D4 Base of Stack
000C 01C4 Initial Program Counter
000E 75D8 Initial Stack Pointer
File "MTX_Monitor_11.spin"
0010 1580 0417 Link to Next Object
0014 01B4 0000 Link to MAIN
0018 0235 0000 Link to GETPOS
<<< Many lines omitted >>>
PUB Main
BufIn := 0
01C4 35 ; Push_0
01C5 41 ; Pop_Varmem_Long_0
BufOut := 0
01C6 35 ; Push_0
01C7 45 ; Pop_Varmem_Long_1
inp.Start (@Buffer, @BufIn, @BufOut)
01C8 01 ; Frame_Call_Noreturn
01C9 8B 20 ; Reference_Variablemem_Byte, $0020
01CB 43 ; Reference_Varmem_Long_0
01CC 47 ; Reference_Varmem_Long_1
01CD 06 1801 061A ; Objcall, $1801, $061A
Text := vdp.GetBuffer
01D0 00 ; Frame_Call_Return
01D1 06 1A02 0006 ; Objcall, $1A02, $0006
FontAddr := vga.FontLoc
01D5 00 ; Frame_Call_Return
01D6 06 1703 0148 ; Objcall, $1703, $0148
<<< Many lines omitted >>>
File "MTX_Video_8.spin"
1590 2BA0 0004 Link to Next Object
1594 2B30 0008 Link to START
1598 2B84 0004 Link to STOP
159C 2B9B 0000 Link to FONTLOC
DAT
000 org
15A0 000 5C7C00A5 video jmp #init0
15A4 001 A2BD47F0 vsync mov tpage1, par wz ' Test for first cog
15A8 002 A0BD47F1 mov tpage1, cnt ' Get current time
15AC 003 082946A2 if_z wrlong tpage1, pagead ' Save time for first cog to hub ram
15B0 004 A0FD4E02 mov line_cnt, #vs ' Number of lines of vertical sync
15B4 005 5CFD0E7B call #blank_vsync ' Output sync pulse
15B8 006 08BD48A2 rdlong tpage2, pagead ' Fetch time of first cog
15BC 007 84BD48A3 sub tpage2, tpage1 ' Time difference between cogs
15C0 008 A0FD4E1A vb_lines mov line_cnt, #vb - 8 ' Number of lines in vertical back porch (cog 0 starts 8 lines early)
15C4 009 5CFD0E7B call #blank_vsync ' Output back porch
<<< Many lines omitted >>>
Variables for TOP (MTX_Monitor_11.spin)
6D9C long BUFIN
6DA0 long BUFOUT
6DA4 long TEXT
6DA8 long TTOP
6DAC long DATPTR
6DB0 long PATPART
6DB4 long FONTADDR
6DB8 long DATCTR
6DBC byte BUFFER[2048]
75BC byte GDAT[6]
Variables for TOP.VGA (MTX_Video_8.spin)
75C4 byte COG[3]
Variables for TOP.INP (MTX_Input_2.spin)
75C8 byte NCOG
75CC Reserved 8 bytes.
75D4 Base of stack.
75E4 Top of stack.
Comments
I hope we can get that 'officially' soon.
Where can I sign the e-petition?
:-D
Just need to fixup the changes he made to the banner when you run it and the readme
I have compiled your source on Linux and found a couple of issues:
1. Without the new -l on the command line it gives a segmentation error (missing some null checks ?)
2. Compiling one of my projects, the listing is a bit weird, I tried to reproduce on a smaller source without success, here is an excerpt:
0000 04C4B400 Frequency 80000000 0004 6F Clock Mode 0005 1E Check Sum 0006 0010 Base of Program 0008 45CC Base of Variables 000A 6630 Base of Stack 000C 0C1B Initial Program Counter 000E 6640 Initial Stack Pointer File "terminal.spin" 0010 15F0 060C Link to Next Object 0014 0C0B 000C Link to MAIN 0018 0F93 0000 Link to GET_NRCS_MAP 001C 0FC9 000C Link to DECODE 0020 10A7 000C Link to KEYPRESSED 0024 12D6 0000 Link to ENTERSETTINGS 0028 12FD 0000 Link to EXITSETTINGS 002C 13A2 0000 Link to UPDATESETTINGS 0030 1511 0000 Link to UPDATEKEYCONFIG 0034 1569 0004 Link to PRINTAT 0038 1585 000C Link to OVERLAYPARAMS 003C 15A5 0004 Link to GET_MAP cursor_save 0040 15F0 1FE0 r_save 0044 23CC 1FE0 usb_buf[64] 0048 257C 2048 usb_report[8] 004C 2B8C 2048 kb_last 0050 3F98 2048 if 0054 43A4 2048 long kb_str_table S_LOCK) 000 K) 0058 000 A0BFEC87 $20) 005C 001 5CFCC659 ar(c) 0060 002 867D2607 peat i from 0 to 11 0064 003 5C680037 ble][i] 0068 004 867D2608 te[@nrcs][i] 006C 005 5C68003E ' Do nothing 0070 006 867D2609 #KeyCapsLock: 0074 007 5C680040 llLock: 0078 008 867D260A kb#KeySpace..kb#KeyMaxCode: 007C 009 5C680044 _str_table][c - kb#KeySpace] 0080 00A 867D260C repeat strsize(ptr) 0084 00B 5C680048 ptr++ 0088 00C 867D260D RI enterSettings 008C 00D 5C680057 cursor.byte[CY] := 0 0090 00E 867D261B or.byte{CM} := (cursor.byte{CM} & constant(!CURSOR_MASK)) | CURSOR_OFF 0094 00F A0A9428D | CURSOR_OFF 0098 010 5C6800A4 1 009C 011 08BD2C91 sor.byte & constant(!CURSOR_MASK)) | CURSOR_ON | ee_config[4] 00A0 012 A0BD2A90 e_config[4] 00A4 013 A0FD2E0C (ee_config[2]) 00A8 014 00BD2495 ] 00AC 015 80FD2A01 ble_1 := @strTable 00B0 016 863D2493 kb_str_table_1 := @strTableApp 00B4 017 00BD2496 00B8 018 80FD2C01 le_1 := @strTableWS 00BC 019 E4D52E14 nfig[6] 00C0 01A A0A92692 _table_2 := @strTable 00C4 01B E1FD1650 2: 00C8 01C 5C4C0020 00CC 01D 877D1818 _1 00D0 01E 80F11801 2]) 00D4 01F 5CCCF464 s_table_1 00D8 020 A0BD2A8C _nrcs_table_2 00DC 021 2CFD2A04 00E0 022 A0BD2C95 ettings 00E4 023 2CFD2C02 0: 00E8 024 80BD2C95 F0, string("US")) 00EC 025 80BD2C8B printAt(7, 44, $F0, string("IT")) 00F0 026 2CFD2C01 tAt(7, 44, $F0, string("UK")) 00F4 027 A0BD2A83 3: 00F8 028 80BD2A96 ng("FR"))
I tried the same command line on other projects and doesn't seems to have issues. This particular source uses conditional compiles (#ifdef, etc.), maybe an incompatibility with that feature ?
A suggestion: allow to write the listing on a file instead of stdout.
Keep up the good work!
Thank you for the pointers. I am still fairly new to the Propeller community, and dit not know what to search for.
@Roy Eltham
Flattered. My main concern at the moment would be the translation of the binary into byte-code mnemonics. In particular the interpretation of the op_Effect and op_Unsigned_Effected_Offset parameters of some byte-codes.
@macca
I will look into this.
Segfault resolved by latest commit.
With the example you show
0040 15F0 1FE0 r_save
is being listed in Object Link format, and thenS_LOCK) 000 K) 0058 000 A0BFEC87 $20)
is being listed in PASM format.Without a copy of the source code I am unable to determine the cause. Are you able to post the source?
Looks like an issue with the conditional compilation, I modified a sample hello world in the spin standard library as follows:
CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 OBJ ser : "com.serial" PUB Main ser.Start(115200) #ifdef TEST ser.Char("H") ser.Char("e") ser.Char("l") ser.Char("l") ser.Char("o") ser.Char(" ") #endif ser.Char("w") ser.Char("o") ser.Char("r") ser.Char("l") ser.Char("d") ser.Char("!") ser.Char(10)
And the listing shows this:
PUB Main ser.Start(115200) 001C 01 ; Frame_Call_Noreturn 001D 3A 01C200 ; Push_Mid_Lit, $01C200 0021 06 0201 3877 ; Objcall, $0201, $3877 EST 0024 01 ; Frame_Call_Noreturn 0025 38 77 ; Push_Byte_Lit, $77 0027 06 0206 386F ; Objcall, $0206, $386F H") 002A 01 ; Frame_Call_Noreturn 002B 38 6F ; Push_Byte_Lit, $6F 002D 06 0206 3872 ; Objcall, $0206, $3872 e") 0030 01 ; Frame_Call_Noreturn 0031 38 72 ; Push_Byte_Lit, $72 0033 06 0206 386C ; Objcall, $0206, $386C l") 0036 01 ; Frame_Call_Noreturn 0037 38 6C ; Push_Byte_Lit, $6C 0039 06 0206 3864 ; Objcall, $0206, $3864 l") 003C 01 ; Frame_Call_Noreturn 003D 38 64 ; Push_Byte_Lit, $64 003F 06 0206 3821 ; Objcall, $0206, $3821 o") 0042 01 ; Frame_Call_Noreturn 0043 38 21 ; Push_Byte_Lit, $21 0045 06 0206 380A ; Objcall, $0206, $380A ") 0048 01 ; Frame_Call_Noreturn 0049 38 0A ; Push_Byte_Lit, $0A 004B 06 0206 0008 ; Objcall, $0206, $0008 File "com.serial.spin"
Attached is a zip file with the sources. Compile with and without -DTEST to see the difference.
Quite right. In order to generate the listing I re-load the source files, and I was not pre-processing them correctly so my text pointers were not pointing to the correct place in the source. Should be corrected by my latest commit.
An alternative would have been to keep an in-memory copy of the source code when it is first loaded by the compiler, however that could have resulted in a substantial increase in memory usage.
You suggested re-directing the listing to a file. I can see some advantages to that, however I am not sure how best to do that on the command line. Possibilities:
* Listing could always go to <source name>.lst
* -l switch could always take a file name, with "-" to select stdout.
* Could use the syntax -l=<filename>
Which would be preferred?
Very good, works perfectly now, even with the other bigger project. Thank you!
While you are at it, check also if there is a similar issue with the -u (unused method elimination), I don't have evidence of any, just to be sure.
With today's available memory I don't see it as a problem, after all OpenSpin won't run on a 64k system and the sources are few hundreths kbytes at most, on complex projects.
For me, a listing should always be written to a file, since it is intended as a way to see what the compiler actually has produced so in my opinion doesn't make sense to have it on stdout at all. Maybe try with multi-letter parameter: -l write to file <output>.lst, -lc write to console output. Also I think it would be better to use the output file name (as specified by -o or default) instead of the source file so it is possible to compile projects with conditional compilation and save the related listing.
Good call. Unused method elimination also causes problems, although for different reasons. This is going to take a bit more work.
Unused method elimination basically does 2 compiles of everything. The first one compiles as "normal" but tracks the info needed to know what's used and what's not, then the second one uses the info while compiling to skip unused stuff.
If you can have your stuff "off" during the first compile, then on for the second one, that should work out I think.
There are now three listing options:
-lc Lists to stdout
-lo Lists to <output>.lst
-lf <filename> Lists to named file.
Other minor changes:
* Variables are now listed with their relative address from the top of the current variables block as well as their absolute address. Spin byte-code references variables by their relative address.
* Minor improvements to the interpretation of the Spin byte-code
Excellent, thank you very much!
Working on it.
The only outstanding item I am aware of is possible incorrect translation of some of the more obscure byte-codes. Even then the bytes listed on the left hand side will be correct, and the translation will recover at the next source code line.
Does anyone have an example of Spin code which generates byte-codes $86, $8A or similar? If I understand correctly these could in principle be followed by an address (1 or 2 bytes), an effects byte and another address (1 or 2 bytes), giving a maximum instruction length of 6 bytes.
% openspin -lf list.lst -I "/Users/myUserName/Library/Documents/PropSpinCode/SpinObjects" VocalTractDemo_mama.spin Propeller Spin/PASM Compiler 'OpenSpin' (c)2012-2018 Parallax Inc. DBA Parallax Semiconductor. Fork by Memotech Bill to add "Annotated Listing" of generated binary. https://github.com/Memotech-Bill/OpenSpin Version 1.01.00 Compiled on May 4 2020 11:39:40 Compiling... VocalTractDemo_mama.spin |-VocalTract.spin Done. Program size is 2220 bytes zsh: segmentation fault openspin -lf list.lst -I VocalTractDemo_mama.spin
% openspin -lo -I "/Users/myUserName/Library/Documents/PropSpinCode/SpinObjects" VocalTractDemo_mama.spin Propeller Spin/PASM Compiler 'OpenSpin' (c)2012-2018 Parallax Inc. DBA Parallax Semiconductor. Fork by Memotech Bill to add "Annotated Listing" of generated binary. https://github.com/Memotech-Bill/OpenSpin Version 1.01.00 Compiled on May 4 2020 11:39:40 Compiling... VocalTractDemo_mama.spin |-VocalTract.spin Done. Program size is 2220 bytes zsh: segmentation fault openspin -lo -I VocalTractDemo_mama.spin
% openspin -lc -I "/Users/myUserName/Library/Documents/PropSpinCode/SpinObjects" VocalTractDemo_mama.spin Propeller Spin/PASM Compiler 'OpenSpin' (c)2012-2018 Parallax Inc. DBA Parallax Semiconductor. Fork by Memotech Bill to add "Annotated Listing" of generated binary. https://github.com/Memotech-Bill/OpenSpin Version 1.01.00 Compiled on May 4 2020 11:39:40 Compiling... VocalTractDemo_mama.spin |-VocalTract.spin Done. Program size is 2220 bytes 0000 04C4B400 Frequency 80000000 0004 6F Clock Mode ... ... ... 0988 002C long CNT_ 098C 0030 long FRAMES[32] zsh: segmentation fault openspin -lc -I VocalTractDemo_mama.spin
The attached code is just Chip's Vocal Tact Demo and driver. I'm running on macOS 10.15.4 (Catalina).dgately
Thank you for your error report. There was a typo in the code for listing the variables for an array of objects, which I have corrected in the latest commit. Hopefully this will resolve the problem.
See the punt/spin latest version (v34r)
I think this is similar to your SpinBytecodeDoc_600_260C_007F.spin from forums.parallax.com/discussion/111684/spin-bytecode which I have been using in conjunction with https://github.com/rosco-pc/propeller-wiki/wiki/Spin-Byte-Code
I just wanted to check my understanding.
' .---.---.---.---.---.---.---.---. '$80-DF Access MEM, OBJ, | 1 | s s | i | b b | o o | (96 stack load / save opcodes) ' VAR and LOC `---^---^---^---^---^---^---^---' ' | | | | ' 00= Byte | | 00= PUSH Read - push result in stack ' 01= Word | | 01= POP Write - pop value from stack ' 10= Long | | 10= USING 2nd opcode (assignment) executed, result in target ' (11= mathop) | | 11= PUSH # Push address of destination onto stack ' | 00= MEM base popped from stack, if i=1 add offset ' | 01= OBJ base is object base , if i=1 add offset ' | 10= VAR base is variable base , if i=1 add offset ' | 11= LOC base is stack base , if i=1 add offset ' 0= no offset ' 1=[]= add offset (indexed)
So if the "i" bit is set this is followed by a one or two byte offset. Then if "oo" = %10, this is followed by a "using" or "effects" byte:
' Assignment Operators (p=push, ss=size: 00=bit, 01=byte, 10=word, 11=long) ' ' This is an additional bytecode and follows a USING bytecode. ' ' p000000- write ' -0000s1- repeat-var loop (s = pop step) +1..2 address ' p00010-- ?var random forward (long) ' p00011-- var? random reverse (long) ' p00100-- ~var sign-extend byte ' p00101-- ~~var sign-extend word ' p00110-- var~ post-clear ' p00111-- var~~ post-set ' p0100ss- ++var pre-inc (mask by size) ' p0101ss- var++ post-inc (mask by size) ' p0110ss- --var pre-dec (mask by size) ' p0111ss- var-- post-dec (mask by size) ' p1sxxxxx math operator (!s = swap binary args)
So if this has the value %-0000s1- this is followed by another address.
Is this plausible or is this a pathological case that would never occur in practice?
What I was really looking for is some short examples of Spin code, which when compiled generated the various forms of byte-code. Is there a test suite for the openspin compiler?
This is your listing:
02FE 8781 word TOKEN_DEV1_EP15, 0 0300 017 0000 DAT heap_begin ' Begin recyclable memory heap 0302 F1140000 org 0306 BF A0 0308 001 A0BFF513 t1 mov frqa, frqa_value 030C 002 A0BFFD15 l_cmd mov vcfg, vcfg_value 0310 003 A0BFFF16 codec_buf mov vscl, vscl_value 0314 004 5CFDA6CE codec_cnt call #enc_reset
This is the listing produced by bstc:
02FE(0016) 81 87 | word TOKEN_DEV1_EP15, 0 0300(0017) 00 00 | 0302(0017) | heap_begin ' Begin recyclable memory heap 0302(0017) | org 0304(0000) | controller_cog 0304(0000) 14 F1 BF A0 | tx_count mov ctra, ctra_value 0308(0001) 13 F5 BF A0 | t1 mov frqa, frqa_value 030C(0002) 15 FD BF A0 | l_cmd mov vcfg, vcfg_value 0310(0003) 16 FF BF A0 | codec_buf mov vscl, vscl_value 0314(0004) CE A6 FD 5C | codec_cnt call #enc_reset
As you can see, at address 0302 there are two filler bytes needed to align the dat section to a long boundary, your listing consider them as part of the following dat section and interpret them as the first instruction, which is wrong. Note also that the controller_cog and tx_count labels are missing. The bstc listing dosn't show the filler bytes but the following instruction is correct.
I'm attaching the source here, it is part of the usb-host controller code from
https://github.com/SaucySoliton/propeller-usb-host/
Best regards,
Marco
I have revised this and openspin now produces:
02F4 014 0000 02F6 AE81 word TOKEN_DEV1_EP13, 0 02F8 015 0000 02FA 3701 word TOKEN_DEV1_EP14, 0 02FC 016 0000 02FE 8781 word TOKEN_DEV1_EP15, 0 0300 017 0000 0302 DAT 0302 heap_begin ' Begin recyclable memory heap 0302 org 0302 00 00 0304 controller_cog 0304 000 A0BFF114 tx_count mov ctra, ctra_value 0308 001 A0BFF513 t1 mov frqa, frqa_value 030C 002 A0BFFD15 l_cmd mov vcfg, vcfg_value 0310 003 A0BFFF16 codec_buf mov vscl, vscl_value 0314 004 5CFDA6CE codec_cnt call #enc_reset 0318 cmdret 0318 005 FC3E311D waitvid v_palette, v_idle
As an aside note, a slightly modified version of the USB code can be found here.
You should look at using the listings from homespun and bst (they each have advantages). Hopefully then you can contrive some examples to generate the required bytescodes and look at their generation. Sorry I can't be of much help.
It turns out that yes it is possible to have two addresses, one preceding and one following the "using" byte:
0010 0074 0002 Link to Next Object 0014 0064 0000 Link to PUB Demo 0018 DAT 0018 000 00000000 pad long 0[20] ' Push subsequent variables down 001C 001 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 005C 011 00000000 00000000 00000000 0068 014 00000000 sum long 0 006C 015 00000000 index long 0 0070 016 0000000A count long 10 0074 PUB Demo 0074 repeat index from 1 to count 0074 36 ; Push_1 0075 C5 5C ; Pop_ObjectMem_Long, $005C 0077 sum += index 0077 C4 5C ; Push_ObjectMem_Long, $005C 0079 C6 58 4C ; Effect_ObjectMem_Long, $0058, Add, swap 007C 36 ; Push_1 007D C4 60 ; Push_ObjectMem_Long, $0060 007F C6 5C 02 74 ; Effect_ObjectMem_Long, $005C, repeat-var loop, $FFF4 0083 32 ; Return
The line at 007F illustrates this: The first address is the location (relative to the start of the object) of the loop variable, and the second address is the offset to the start of the loop ($0083 + $FFF4 = $0077 in signed arithmetic).
In this case, because the addresses are small they have been encoded as single bytes.
@Cluso99, one tiny piece of new information, the address following a "using" code of "-0000s1-" is a signed offset.