SX/B SEROUT vs PBasic SEROUT
Bill Chennault
Posts: 1,198
All--
I am working on a project involving an AP-16+. I would LIKE to control it with an SX using SX/B. However, I have just realized that SEROUT is vastly different between PBasic and SX/B.
PBasic: SEROUT pin, baudmode, data
SX/B: SEROUT pin, baudmode, value
There is a big difference between "data" and "value"! PBasic allows a list of variables, constants, expressions, and formatters as data. SX/B restricts data to a value between 0 - 255. Sending commands to the AP-16+ requires a PBasic SEROUT such as this:
SEROUT pin, baudmode, ["!AP16", Addr, "PW", "birdie", CR, 1] (Note the complex data argument.)
Is there a way to emulate that in SX/B? (Or, should I just use a Stamp?)
Thanks!
--Bill
I am working on a project involving an AP-16+. I would LIKE to control it with an SX using SX/B. However, I have just realized that SEROUT is vastly different between PBasic and SX/B.
PBasic: SEROUT pin, baudmode, data
SX/B: SEROUT pin, baudmode, value
There is a big difference between "data" and "value"! PBasic allows a list of variables, constants, expressions, and formatters as data. SX/B restricts data to a value between 0 - 255. Sending commands to the AP-16+ requires a PBasic SEROUT such as this:
SEROUT pin, baudmode, ["!AP16", Addr, "PW", "birdie", CR, 1] (Note the complex data argument.)
Is there a way to emulate that in SX/B? (Or, should I just use a Stamp?)
Thanks!
--Bill

Comments
Now that won't take insane amounts of space, but of course, you are wondering, "is there an even more efficient way?" Sure, but this is meant to be clear. If it were me, I would wrap the preamble and postamble (is that word?) for all the AP16 calls into yet another sub:
Now, *if it were me* I would set up the file names as "data" strings, and write one more sub that would let me pass just an index number (say 0-32) and then the sub would "look up" the data string for the wav file name, and write all the bytes for that filename. This would save even more code space and clean up my code visually quite a bit.
Hope this helps....
Yes. It helps a lot. Thank you very much!
--Bill
The SX48/AP-16+ combination is working. But, I am trying to implement "phase iii" of Zoot's guidance using data statements in SX/B. So far, without success. Will you take a look at my code, which is all stolen and adapted from others (primarily, Zoot) and tell me what might be wrong? (I will eliminate the redundant calls later.) My immediate goal is to read the file names from data statements in SX/B. If I can do that, then I will implement the AP-16+ pre- and post- (Zoot, if you see it on the Internet, then it must be true) amble code.
Thanks!
--Bill
ps Is there a way to avoid the "X" on an attached file? It's ugly. Additionally, my code listing is proportionately spaced. How do I fix it?
Is this what you're looking for?
' ------------------------------------------------------------------------- ' Subroutine Declarations ' ------------------------------------------------------------------------- TX_BYTE SUB 1, 2 ' transmit byte TX_STR SUB 2, 4 ' transmit string ' ------------------------------------------------------------------------- ' Program Code ' ------------------------------------------------------------------------- ' ------------------------------------------------------------------------- ' Subroutine Code ' ------------------------------------------------------------------------- ' Use: TX_BYTE theByte {, count} ' -- transmit "theByte" at "Baud" on "SOut" ' -- optional "count" may be specified (must be > 0) SUB TX_BYTE tmpB1 = __PARAM1 ' save byte IF __PARAMCNT = 1 THEN ' if no count tmpB2 = 1 ' set to 1 ELSE ' otherwise tmpB2 = __PARAM2 ' get count IF tmpB2 = 0 THEN ' do not allow 0 tmpB2 = 1 ENDIF ENDIF DO WHILE tmpB2 > 0 ' loop through count SEROUT SOut, Baud, tmpB1 ' send the byte DEC tmpB2 ' decrement count LOOP ENDSUB ' ------------------------------------------------------------------------- ' Use: TX_STR [string | label] {, count {, offset }} ' -- "string" is an embedded string constant ' -- "label" is DATA statement label for stored z-String ' -- "count" is number of characters to send (0 is entire string) ' -- "offset" is offset from head of string (0 is head) SUB TX_STR tmpW1 = __WPARAM12 ' get offset+base tmpB3 = 0 ' do whole string IF __PARAMCNT >= 3 THEN ' count specified? tmpB3 = __PARAM3 ' -- yes, get count ENDIF IF __PARAMCNT = 4 THEN ' offset specified? tmpW1 = tmpW1 + __PARAM4 ' -- yes, update it ENDIF DO READ tmpW1, tmpB4 ' read a character IF tmpB4 = 0 THEN EXIT ' if 0, string complete TX_BYTE tmpB4 ' send character DEC tmpB3 ' update count IF tmpB3 = 0 THEN EXIT ' terminate if done INC tmpW1 ' point to next character LOOP ENDSUBCan not see an "X" on you attached file.Try (Code)(/Code) not (CODE)(/CODE).
hope it helps
I'm not sure I would use a version with an optional "count" param if you don't need it.
In any case, in the example above:
Is "like" having data that reads like the following, and the address of the data is automatically passed by TX_STR:
So both of the following are functionally identical:
For your project I would do the strings as data statements, because then you can use the address for whatever clip you want to play (use a lookup statement or if/then, etc) and have the appropriate string fed out.
One comment, however, about your posted code that I don't quite get -- you "GOTO Start". I do not recommend this. The automatically inserted code that comes after "Start" in SX/B is initialization code that configures pins, clears memory, etc. Generally, you want a structure more like this:
I am studying your suggestions, now. Thank you for helping me.
This is the first time I have used DATA statements in SX/B. They kinda remind me of cutting assembly code on an 8080. But, that was a LONG time ago and I wish I could remember more of it!
We are right on the northern edge of the big storm in the mid-west. It is headed north and east. It looks like it will hammer KC. Sure is pretty, though. However, the view of the golf course is disappearing as the snow falls harder and harder . . .
--Bill
I neglected to add that I will stick 'Main:' back in! I took it out because I FORGOT its importance and wanted to save listing space in my posted code segment.
--Bill
I studied your examples. Is there another example which you think would help me?
Progress is being made; I am successfully using DATA statements. I am not quite ready to beg for help.
If there is another example, let me know where to begin my search.
Thanks!
--Bill
they are not my examples.
I got the example In the SX/B help, (SX-Key Editors / "SX/B Help")
I search for "SEROUT",
and got the "READ Example", and posted some of it here for you.
A good place to begin your search is the (SX-Key Editors / "SX/B Help"), there's a lot of good stuff in there.
For you or anyone that don't have the same version of the "SX/B Help" or can not fined the "READ example",
here it is:
' ------------------------------------------------------------------------- ' Program Description ' ------------------------------------------------------------------------- ' ' This program demonstrates the use of strings in an SX/B program. As of ' version 1.4, the SX/B READ instruction can accept a variable base and ' offset. These values may be created by the compiler by specifying the ' label of a stored z-string, or by using an inline string constant. ' ' The subroutine TX_STR accepts the base and offset values of a stored or ' inline string and will transmit them to a connected terminal -- the ' construction of the subroutine allows the string to cross SX page ' boundaries. ' ------------------------------------------------------------------------- ' Device Settings ' ------------------------------------------------------------------------- DEVICE SX28, OSCXT2, TURBO, STACKX, OPTIONX FREQ 4_000_000 ID "READ_STR" ' ------------------------------------------------------------------------- ' IO Pins ' ------------------------------------------------------------------------- SOut PIN RA.0 OUTPUT ' serial output ' ------------------------------------------------------------------------- ' Constants ' ------------------------------------------------------------------------- Baud CON "T9600" ' use with MAX232/USB2SER CR CON 13 ' carriage return LF CON 10 ' line feed ' ------------------------------------------------------------------------- ' Variables ' ------------------------------------------------------------------------- tmpB1 VAR Byte ' subroutine work vars tmpB2 VAR Byte tmpB3 VAR Byte tmpB4 VAR Byte tmpW1 VAR Word ' ========================================================================= PROGRAM Start ' ========================================================================= ' ------------------------------------------------------------------------- ' Subroutine Declarations ' ------------------------------------------------------------------------- TX_BYTE SUB 1, 2 ' transmit byte TX_STR SUB 2, 4 ' transmit string ' ------------------------------------------------------------------------- ' Program Code ' ------------------------------------------------------------------------- Start: PLP_A = %0001 ' pull-up unused pins PLP_B = %00000000 PLP_C = %00000000 Main: TX_STR "SX/B makes string output easy!" ' send inline string TX_STR CrLf ' send stored z-string TX_STR TestStr, 14 ' sub-string TX_STR CrLf TX_STR TestStr, 13, 16 ' sub-string at offset TX_STR CrLf TX_STR TestStr, 8, 31 TX_STR Version TX_STR CrLf TX_BYTE LF, 2 PAUSE 1000 GOTO Main ' ------------------------------------------------------------------------- ' Subroutine Code ' ------------------------------------------------------------------------- ' Use: TX_BYTE theByte {, count} ' -- transmit "theByte" at "Baud" on "SOut" ' -- optional "count" may be specified (must be > 0) SUB TX_BYTE tmpB1 = __PARAM1 ' save byte IF __PARAMCNT = 1 THEN ' if no count tmpB2 = 1 ' set to 1 ELSE ' otherwise tmpB2 = __PARAM2 ' get count IF tmpB2 = 0 THEN ' do not allow 0 tmpB2 = 1 ENDIF ENDIF DO WHILE tmpB2 > 0 ' loop through count SEROUT SOut, Baud, tmpB1 ' send the byte DEC tmpB2 ' decrement count LOOP ENDSUB ' ------------------------------------------------------------------------- ' Use: TX_STR [string | label] {, count {, offset }} ' -- "string" is an embedded string constant ' -- "label" is DATA statement label for stored z-String ' -- "count" is number of characters to send (0 is entire string) ' -- "offset" is offset from head of string (0 is head) SUB TX_STR tmpW1 = __WPARAM12 ' get offset+base tmpB3 = 0 ' do whole string IF __PARAMCNT >= 3 THEN ' count specified? tmpB3 = __PARAM3 ' -- yes, get count ENDIF IF __PARAMCNT = 4 THEN ' offset specified? tmpW1 = tmpW1 + __PARAM4 ' -- yes, update it ENDIF DO READ tmpW1, tmpB4 ' read a character IF tmpB4 = 0 THEN EXIT ' if 0, string complete TX_BYTE tmpB4 ' send character DEC tmpB3 ' update count IF tmpB3 = 0 THEN EXIT ' terminate if done INC tmpW1 ' point to next character LOOP ENDSUB ' ========================================================================= ' User Data ' ========================================================================= TestStr: DATA "Parallax, Inc. SX/B Compiler Version 1.50", 0 Version: DATA "1.50", 0 CrLf: DATA CR, LF, 0I've studied that example. It is a good example of using the READ/DATA statements. But, what I am trying to do is slightly more complex. (At least, it is to me!)
What I would like to do--or rather what my "mission" has morphed into--is READ multiple, 0-terminated strings, like this . . .
The current problem concerns my inability to address the second string. I can read the first string a byte at a time. However, I cannot read the next string. I have a byte-sized offset variable that points to the first and then successive bytes in the string. It works well until the second string at which point it SEEMS to be reset to 0, thus making READ string+offset re-read the first string beginning with byte 0. This is all in a loop which tests the current character for 0 (not "0"), calls a sub-routine which performs SEROUT, and increments the offset variable. (I seem to lose the value of the offset variable after the loop is exited upon reading 0.)
I'm still testing. I sure appreciate you watching this thread. There are a few more things I need to pin down/clean up in my code before I can ask intelligent (for me, anyway) questions.
Maybe by just WRITING this it will cause me to think about what I am doing wrong. We'll see.
--Bill
For addressing multiple strings, remember that the start address of each needs to be referenced OR you need to make sure all strings are the exact same length. In the case of wav files on an SD card, I would go with the latter, and give all my wavs 14 character filenames including the .wav -- this would give me "blocks" of string datasets that are all 16 characerters (14 characters for the filename + 1 CR + 1 "zero" terminator. Then I could call each string to send by using a number from 0-x and multiplying by 16 to get the address, e.g.
Now, if you want variable length strings, that's fine, but you need an extra step where you have a table that is the addresses....then you read the "address" by an index number (like above) then load a read address with the "read" address of the start of the string.
I wonder if my concept of referencing the start address of each string is incorrect? Assumption #1: I THOUGHT all I had to do was reference the start address of the 0 byte of the first string and then add an offset for each ensuing byte of the ENTIRE DATA "block." My included code illustrates my take on this.
The AP-16+ is an 8.3 machine. So, couldn't I just use a byte variable to point to the next character as long as the total number of characters <= 255 (and Assumption #1 is also correct)?
If Assumption #1 is incorrect, then we have found my error. The following listing is simple, really. Especially since it has been seen before as posted by the real authors from whom I stole, merged and then mangled it.
--Bill
I struggled with the code some more to no avail. The listing above is still my "best" effort.
In execution, the AP-16+ plays "sfx00" 13 times. It never plays the next file, "sfx02". Perhaps, I am handling the AP-16+ incorrectly. Maybe JonnyMac might chime in. He already labored to overcome a previous stupido on my part with the AP-16+.
I should add that the AP-16+ Stamp Demo code works fine for me using either a BS2 or a BS2p40.
--Bill
For one, I moved the AP16 reset stuff into the start section, so it only runs once. You can change it when/if everything else works, but I don't think you need to send that every time.
Then I found the bug -- sorry I should've seen this earlier. Your do...read...ser_out...loop increments idx (read offset variable) AFTER the exit is evaluated, so on the final char (0), it will be in the wrong spot next time around and will just keep exiting.
If you still have problems, definitely check simple stuff -- correct oscillator and feedback (parallel) resistor across osc1 and osc2, etc. You could also try communicating with the AP16 at 2400 baud, get it working, then try higher baud.
Thank you all for helping me!
Zoot, it works perfectly now, as you know! (I was close, but "close" doesn't cut it in this game.)
I appreciate all the time all of you spent thinking about this for me.
--Bill
P.S.
The "0" in "sfx00" is not 0 binary (i.e. it is not %0000_0000), it is ASCII character "0". Check your ASCII chart. This is why 0 term strings work -- the 0 index position in ASCII is not a "character" in the sense of "0-9" or "abc".