Forums

Sega Master System / Mark III / Game Gear
SG-1000 / SC-3000 / SF-7000 / OMV
Home - Forums - Games - Scans - Maps - Cheats - Credits
Music - Videos - Development - Hacks - Translations - Homebrew

View topic - SC-3000 Tape Emulation (was: SC3000 Basic Update?)

Reply to topic
Author Message
  • Joined: 18 Jun 2005
  • Posts: 42
  • Location: Wairarapa, New Zealand
Reply with quote
SC-3000 Tape Emulation (was: SC3000 Basic Update?)
Post Posted: Wed Oct 19, 2005 7:49 pm
Hi there, I've written an application that interprets a wave file that was constructed by ripping a cassette based program. Is there any chance you could add an emulator command that will load the resulting binary file into MEKA memory from position &H9800 onwards and execute it if they have the SEGA Basic (16k/32k version) ROM file loaded so that users can enjoy some of the excellent non-cartridge software that's been written for the SEGA?
  View user's profile Send private message
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14745
  • Location: London
Reply with quote
Post Posted: Wed Oct 19, 2005 8:41 pm
The preferred way to emulate this stuff would be to make it loadable through the original BASIC CLOAD etc commands, but that might be possible in addition to a quick RAM load. I presume the decoded files you have are pure RAM images, with no additional data; can the original wave be reconstructed from that? From what I saw previously, it might be necessary to prepend the filename chunk; how about including that in your dumps?

There's a strong chance you could also convert the tape code to SF-7000 disks, but having 100% digital backups of original tapes is better.

I hope you'll publish your information so we can also have possibilities like loading directly from WAVs and from line-in, and of course re-encoding to WAV and line-out.
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jun 2005
  • Posts: 42
  • Location: Wairarapa, New Zealand
Reply with quote
Post Posted: Wed Oct 19, 2005 9:20 pm
Wow, great response time!

1. The filename chunk doesn't appear to be necessary once the program is in memory so can be ignored if we're looking at a straight RAM load. I can also generate a binary dump that includes the lead-ins and the filename chunk which will be fine for simulated cassette loading but as some of the machine code games have a loader I don't think that method will work as the user won't have time to interact before the next chunk is loaded. I was aiming to just create a binary file containing the actual executable game and ignore the prelim chunks (loading screens, special chunk loaders, etc).

2. I can reconstruct a wave file from the deconstructed wave.

3. I was also aiming to write a windows editor that loaded from/saved to a wave file or, alternatively just read/write directly with the SEGA R/W ports and cut out the middle main wave file completely.

4. Another alternative I'm looking at is just tacking the resident program directly on the end of the basic .sc file (with 6144d / &H1800 bytes of zeros inbetween) to see if the emulator would perhaps load the basic rom AND the program at once.

5. I was fully intending to publish all my development and findings once I've got the app just right <grin> I'm writing it in c# but being a vb developer for years I'm trying to learn the ropes at the same time...
  View user's profile Send private message
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14745
  • Location: London
Reply with quote
Post Posted: Wed Oct 19, 2005 10:50 pm
One of the aims of ROM dumping, and by extension disk and tape dumping, is to preserve the exact information that was originally there - no more, no less. Disk dumps include everything - FAT, unused clusters, etc. Tape dumps should include everything, including filenames. Emulators should then be able to load them instantly into RAM by decoding the tape, or through the CLOAD emulated mechanism which is presumably loading them into memory a byte at a time.

I'm not sure what this loader issue you describe is, since I have minimal experience with actually using games on tape. (Every tape-based computer I had as a child was crappy, second-hand and broken.)

Tacking a program onto the .sc binary is bad (creating images that dont reflect reality) and won't work (emulators treat .sc data as ROM, and won't arbitrarily copy it to RAM). However, it wouldn't be hard to write a small loader that copied the program to RAM and ran it.

I've got plenty of C# experience if you need any help. Just keep away from the evil VB and there may still be hope for you.
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jun 2005
  • Posts: 42
  • Location: Wairarapa, New Zealand
Reply with quote
Post Posted: Wed Oct 19, 2005 11:21 pm
hahaha, vb may be evil but at least it's user friendly - anyway, we won't get in to that, I've had enough "discussions" about the pros and cons of both over the years...it all ends up as 1's and 0's at the end of the day...

Okay, I did a test and appending a basic program to the basic rom didn't work and I wasn't comfortable doing that anyway.

I agree the bin file should mirror the source but it means a lot more work for the emulator. An example is, IMHO, the best game for the SEGA, Sir Rodericks Quest, there is a filename chunk which, when displayed during load actually clears the screen, prints the text RUN and leaves the cursor on that line. The program then consists of a hidden loader that, when the user hits CR, loads the actual machine code chunk from cassette. If the user doesn't pause the player or hit CR in time then the loader will miss the beginning of the chunk that defines the actual game. My app actually detects the filename chunk then reads the program chunk (both prefixed with known codes) and if there is another unknown chunk it knows that the program being loaded isn't BASIC so doesn't try to convert the hex to BASIC source.

CLOAD is a diskbasic command as far as I can remember and isn't native to the sega basic level IIIb cart.

In summary, the files are stored in *reverse order bits*, (bits 0 to 7 run left to right) with a low frequency wave signifying a 0 and a high frequency wave (two short waves which in total equal the length of the LF wave length) signifies a 1. The lead-in consists of about 3600 1's which equates to about a 3 second "squeal". The filename chunk starts with &h16 (22d) and the program chunk &h17 (23d). A data byte is 11 bits in length and consists of a leading "0" then 8 data bits in reverse order then a trailing "11".

I can send you a commercial basic game called mars adventurer to test your emulator with if you like?
  View user's profile Send private message
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14745
  • Location: London
Reply with quote
Post Posted: Thu Oct 20, 2005 12:15 am
I ought to make it clear that Meka is not my code, I'm just interested in the technicalities here and hopefully it'll answer others' questions too.

In your Sir Rodericks Quest example, is the "hidden loader" all in the filename chunk? I think it can't be, so it must be in the program chunk, but by the time that's loaded, what is left to do? I think I'm just too unfamiliar with how these things work.

I do know a little bit about wire-level networking, which is why I know the stream of 1s is to allow the hardware clock to be calibrate with the tape, and as I mentioned privately before the data encoding is RS232 compliant. (The data I saw before was also nibble-swapped from RS232, but maybe that was some processing it'd been through.)

I'm fairly sure the TZX format could be used to store the data in a "standard" format. This is also necessary in the cases where the game involves loading data from cassette, as opposed to those games that fit entirely into RAM.

I feel it is better to use a single format rather than releasing games in both tape-accurate and memory-dump formats, since we have the rare opportunity to learn from other systems' mistakes before anything is released. I'll leave it here for others (Bock?) to comment on.
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jun 2005
  • Posts: 42
  • Location: Wairarapa, New Zealand
Reply with quote
Post Posted: Thu Oct 20, 2005 12:48 am
I'm at work and don't have access to my personal emails so haven't seen your private message yet sorry.

You're right, the lead-in is for calibration and yes, it's RS232 compliant and the checksum method is a standard byte sized, complement approach.

The special loader is embedded in the program chunk but as the first line of the program contains an ascii char that clears the screen you can't use LIST to see the content but I cracked that back when I was 12 and right into hacking and machine code programming so I figured out that all the program does is load the chunk of raw data into memory from cassette then executes a CALL <addr> to seemingly automatically start the game. If you look at the wave file you will notice 3 chunks of data, the filename, the loader and finally the actually game.

I'm not familiar with that other format and was just aiming for my app to produce the raw game data for the emulator then hope someone will modify the emulator to load it into memory if the sega basic rom is loaded....:-)
  View user's profile Send private message
  • Site Admin
  • Joined: 08 Jul 2001
  • Posts: 8652
  • Location: Paris, France
Reply with quote
Post Posted: Thu Oct 20, 2005 9:05 am
I'll work on adding proper tape support on MEKA. It's something I ought to do since a long time, perhaps it's time now :)

I agree with Maxim on doing it properly. I don't mind adding an integrated hack into the emulator that would replicate the work of the tools you made (scanning and injecting data in RAM directly).

I'm not just why you insist on having those after-load images. If i understood it properly from what I've read before, this byte/bit encoding you mentionned is used in the whole tape, so your conversion tool could as well extract the whole data, not only data after the header. Then the possibly-to-be-made hacked loader would skip header.

Please publish or e-mail me your information and I'd be glad to give it a go (maybe this weekend, I can work on that instead of adding insane features to MEKA debugger).
  View user's profile Send private message Visit poster's website
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14745
  • Location: London
Reply with quote
Post Posted: Thu Oct 20, 2005 9:05 am
I was referring to our brief email exchange a while back. I'm hoping one of the tape-decoding utilties from other systems will match the data you have. The kind of custom loader you describe can be stored in TZX and thus the original tape data, with the loader, can be reproduced and recorded back to tape, which is ultimately the aim of any tape backup format.

For now, can you test if CSW can compress the tape dumps? If so, it will be easier to share the raw data and try it with existing tools.
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jun 2005
  • Posts: 42
  • Location: Wairarapa, New Zealand
Reply with quote
Post Posted: Thu Oct 20, 2005 10:03 am
I'll give it a try if I have time this weekend. Otherwise I don't mind physically sending a cd with a bunch of archived games on it - I plug my sega sc3000h into the audio out port of my laptop and load them fine and my application also dumps from them so they should be good enough for you guys to test with too.

I'll let you know how I get on.
  View user's profile Send private message
  • Site Admin
  • Joined: 08 Jul 2001
  • Posts: 8652
  • Location: Paris, France
Reply with quote
Post Posted: Thu Oct 20, 2005 10:28 am
Personnally I have gazillions of tape audio dumps (in wav/mp3) that Aaron Wheeler sent me. I'll upload some for whoever need them.

Does anything prevent in dumping whole tape in binary (convert to 0/1, checksums, padding bits after each bytes included) without looking/analyzing at the content?

And THEN it's the emulator/tool job to emulate this, or convert to a
BASIC source code, or inject into running Sega Basic, etc etc.


(sent you a mail directly now)
  View user's profile Send private message Visit poster's website
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14745
  • Location: London
Reply with quote
Post Posted: Thu Oct 20, 2005 1:29 pm
The problem, as I understand from reading various Spectrum etc tape dumping pages, is that for cases where the program does not use the standard casette loading routines from the BASIC ROM, it is necessary to include timing information. For example, in this custom loader case: it is likely that the loader's data format is different to the "normal" format, regarding start/stop, checksums, etc. It may be at a higher data rate, or even encrypted. The time gap during which the loader must be run has to be stored, although it'd be preferable to have some kind of hack to make it auto-run.

I'm just anxious to make sure that the data format is capable of storing all tape data accurately, and that we are not discarding original information (like filenames, loaders, etc) due to focussing on just having the main data (the game).
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jun 2005
  • Posts: 42
  • Location: Wairarapa, New Zealand
Reply with quote
Post Posted: Thu Oct 20, 2005 7:16 pm
Aaron gets around doesn't he - that's who I got all my wave dumps off, he only lives about 20 minutes drive from me but I still haven't met him! <grin>

The custom loader isn't as technical as that, it just reads the raw data from the port and puts it in a specified position in RAM, timings and stuff doesn't come into it so no need for concern with that.

It's no problem for me to convert my application to just read the actual data (ignoring leading and trailing silence to save space) and output just the 1's and 0's if that's what you want. I'll try and find time this weekend although I've got a bachelor party tonight and also there's a NZ vs Australia rugby league test in Auckland and the national rugby union division 1 and 2 finals tomorrow - you guys must understand how important those are to us Kiwi's so I'll be a little under the weather Sunday but we have our Labour Day holiday on Monday so I might be able to do it then and send you the source.
  View user's profile Send private message
  • Site Admin
  • Joined: 08 Jul 2001
  • Posts: 8652
  • Location: Paris, France
Reply with quote
Documentation
Post Posted: Sat Oct 22, 2005 1:57 pm
Btw I took the time to upload some documentations:

SC-3000 Tape Format Notes
Four scanned pages from manual with notes about SC-3000 tape format.

ZIP will all 4:
http://www.smspower.org/dev/docs/tape/sc3000-tape_format_notes.zip

The separates files:
http://www.smspower.org/dev/docs/tape/%5b162%5d%20Format%20recording%20tape.jpg
http://www.smspower.org/dev/docs/tape/%5b163%5d%20Format%20recording%20tape.jpg
http://www.smspower.org/dev/docs/tape/%5b164%5d%20Format%20recording%20tape.jpg
http://www.smspower.org/dev/docs/tape/%5b165%5d%20Format%20recording%20tape.jpg

Gianluca Pisano scanned those from a Sega manual some years ago.
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jun 2005
  • Posts: 42
  • Location: Wairarapa, New Zealand
Reply with quote
Post Posted: Sat Oct 22, 2005 8:35 pm
Wow, if only I had those a few months ago!!! I had to figure all that out the hard way, lots of analysis....oh well, nevermind. The interesting page is the last one where a machine code file format is specified, the reason it's interesting is because it implies that there's a way to save in that format using the SEGA but the machine code games I've analysed aren't saved that way, they have a filename block (starting with &H16) and a short program block (starting with &H17) then blocks of raw data so I don't know what to make of that but nevermind.

Thanks again for those scans - do you know what manual they came from?

I'll try hard to modify my app today but I can't guarantee anything, I have a lot of chores to do around the house.
  View user's profile Send private message
  • Site Admin
  • Joined: 08 Jul 2001
  • Posts: 8652
  • Location: Paris, France
Reply with quote
Post Posted: Sun Oct 23, 2005 2:48 pm
If I go the low-level emulation route, all those custom header/format does not matter : it's the SC-3000 software job to understand this.
I'm think I'm gonna do that first (emulate from a .WAV/MP3 file) then later we can see about working on a more packed format (probably reuse and extend TZX if needed).
What is interesting thought is to be able to extract BASIC source code from a WAV directly. But in the end (in years :) when emulator will support RS232 emulation one could directly print listings to a terminal and save them.
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jun 2005
  • Posts: 42
  • Location: Wairarapa, New Zealand
Reply with quote
Post Posted: Sun Oct 23, 2005 7:45 pm
Sorry about the delay but after further testing I need to recode the HF/LF detection logic, some of the signals are thwarting my current logic and even though the file quality is questionable the SEGA manages to load okay so I'll aim to get that done today. It's a public holiday here in NZ which means my wife is home as well so I may be called away to help with more work around the house :-( please continue to be patient, although I imagine now that you have the spec for how a file's stored you'll probably write your own interpreter, if that's the case I'd be interested in seeing what you come up with...
  View user's profile Send private message
  • Joined: 18 Jun 2005
  • Posts: 42
  • Location: Wairarapa, New Zealand
Reply with quote
Post Posted: Wed Oct 26, 2005 10:11 pm
Hopefully you received the email I sent you Bock, it's got the source code attached...let me know your thoughts, I apologise in advance, I know it's not as slick as it could be but at least it's a starting point for you.
  View user's profile Send private message
  • Joined: 08 Aug 2005
  • Posts: 203
  • Location: Italy
Reply with quote
Post Posted: Thu Feb 02, 2006 8:56 am
Hi to all,
I've tryied to convert the wave form of a very simple program to raw text data with "GoldWave" program.
I've written a simple program to convert to binary data the numerical wave form, cleaned the data list from the leader block and analized the data block.

This is the BASIC program list:

10 CLS
20 PRINT "CIAO"

Theese are the bytes extracted from the tape code (already cleaned from the leader field, and the open/close bits of the bytes)


11101000 23 
10000000 1 
01010000 10

00000000 0
00000000 0
00000000 0
00110101 172 ¬
10110000 13
00010000 8 
00101000 20 
00000000 0
00000000 0
00000000 0
10001001 145 ‘
00000100 32
01000100 34 "
11000010 67 C
10010010 73 I
10000010 65 A
11110010 79 O
01000100 34 "
10110000 13
00000000 0
01000000 2 
00000000 0

What we can see is the 23 (17H) byte, the program part, last 3 bytes of Parity byte and 2 bytes of Dummy Data.

11101000 23  Key Code 17H

10000000 1  Program block
01010000 10

00000000 0
00000000 0
00000000 0
00110101 172 ¬
10110000 13

00010000 8 
00101000 20 
00000000 0
00000000 0
00000000 0
10001001 145 ‘
00000100 32
01000100 34 "
11000010 67 C
10010010 73 I
10000010 65 A
11110010 79 O
01000100 34 "
10110000 13

00000000 0 Parity
01000000 2  Dummy data
00000000 0

Let's see the program part...
we can see a byte wich repeats decimal 13, which in ASCII code is Carriage return. so... let's separate the bynary code.

10000000 1 
01010000 10

00000000 0
00000000 0
00000000 0
00110101 172 ¬
10110000 13

00010000 8 
00101000 20 
00000000 0
00000000 0
00000000 0
10001001 145 ‘
00000100 32
01000100 34 "
11000010 67 C
10010010 73 I
10000010 65 A
11110010 79 O
01000100 34 "
10110000 13

Now we can reat one unknown byte and one wich reminds us the line number... 1,10 and in the second block 8,20.... so in the (we can call basic program line block) we know that the first byte means something which describes the line of basic program.
Then there is one byte which is the line number.
We can read also 3 bytes = 0... what i can think is: if i use just one byte for a line number i can have only 256 lines in a basic program so probably those bytes = 0 are used to specify line numbers > 256.
Let's separate the binary block again.

10000000 1 

01010000 10

00000000 0
00000000 0
00000000 0

00110101 172 ¬
10110000 13



00010000 8 

00101000 20 
00000000 0
00000000 0
00000000 0

10001001 145 ‘
00000100 32
01000100 34 "
11000010 67 C
10010010 73 I
10000010 65 A
11110010 79 O
01000100 34 "

10110000 13

In a Basic program line we have: 1 byte, 4 byte (line number), n bytes (text aarea), 1 byte (13 dec. -> CR)
let's see the first line block and separate the 13 Dec.

10000000 1  Description byte

01010000 10
Line number Bytes: 10
00000000 0
00000000 0
00000000 0

00110101 172 ¬ CLS

10110000 13 CR

In the first line the only code which represents the "CLS" command is 172... what i can think is that is a pointer to the ROM where is the CLS Command name.
If we read the second block, we can read the text: ([space]"CIAO") used in the program list...

00010000 8  Description byte

00101000 20  Line number Bytes: 20
00000000 0
00000000 0
00000000 0

10001001 145 ‘ PRINT

00000100 32
01000100 34 "
11000010 67 C
10010010 73 I
10000010 65 A
11110010 79 O
01000100 34 "

10110000 13 CR

The only byte which remains is: 145... this may be "PRINT" command.
So somewhere, we should find the list of commands.
If we open the meka with BASIC Level III A we can see in the Memory Editro in the ROM section at address: 17C0 the list of the commands and after the CLS command (address 1890) the byte "&hAC" which dec is 172!!!! Here it is.... so at the same way the PRINT command IS &H91 which is 145!!!!
what is stil to know is Why the byte pointer of the commands is after the command itself and what does the first byte ef the line block.
Hope that will help in developing tape emulation in MEKA.
Bye!
  View user's profile Send private message Visit poster's website
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14745
  • Location: London
Reply with quote
Post Posted: Thu Feb 02, 2006 9:09 am
This is pretty much the same as what kerrjnr has already found - it's just not been made public yet. He also figured out more block header types, and we have the documentation linked above which also explains a lot of it. Finding the BASIC command table values won't be particularly hard, and is not necessary for emulation, but still useful if someone wants to make a PC-based BASIC editor for SC-3000 BASIC programs.

Might I suggest that the first byte (before the 32-bit line number) is in fact the size of the following data, in bytes, not including the <CR>?
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jun 2005
  • Posts: 42
  • Location: Wairarapa, New Zealand
Reply with quote
Post Posted: Thu Feb 02, 2006 9:58 am
Maxim is right and sorry for not updating everyone but I thought we would've had a MEKA update by now as I sent my (dodgy) wave to binary SEGA decoder thingy to Bock ages ago, he's obviously busy.

Anyway, I've got it all figured out and the bittersweet thing is that (bitter cause it was frustrating to find out after I'd done all the hard work and sweet cause it was fun to relive my old hacking days) all of the information you need is either online in various doc dumps on SEGA sites or, if you have them, in several SEGA fanclub magazines (NZ publications).

If anyone wants the information I gathered in my quest to decode SEGA cassette dumps then email me - I'll type it all up in an easy to read document and send it out, if I get a lot of requests I'll post it on my website.

Here's a teaser, this is the basic command list and their decimal codes formatted for use in my application:

<?xml version="1.0" encoding="utf-8" ?>
<cmds>
<standard> <!-- These codes are one byte only. -->
<cmd id = "130" text = "LIST"/>
<cmd id = "131" text = "LLIST"/>
<cmd id = "132" text = "AUTO"/>
<cmd id = "133" text = "DELETE"/>
<cmd id = "134" text = "RUN"/>
<cmd id = "135" text = "CONT"/>
<cmd id = "136" text = "LOAD"/>
<cmd id = "137" text = "SAVE"/>
<cmd id = "138" text = "VERIFY"/>
<cmd id = "139" text = "NEW"/>
<cmd id = "140" text = "RENUM"/>
<cmd id = "144" text = "REM"/>
<cmd id = "145" text = "PRINT"/>
<cmd id = "146" text = "LPRINT"/>
<cmd id = "147" text = "DATA"/>
<cmd id = "148" text = "DEF"/>
<cmd id = "149" text = "INPUT"/>
<cmd id = "150" text = "READ"/>
<cmd id = "151" text = "STOP"/>
<cmd id = "152" text = "END"/>
<cmd id = "153" text = "LET"/>
<cmd id = "154" text = "DIM"/>
<cmd id = "155" text = "FOR"/>
<cmd id = "156" text = "NEXT"/>
<cmd id = "157" text = "GOTO"/>
<cmd id = "158" text = "GOSUB"/>
<cmd id = "159" text = "GO"/>
<cmd id = "160" text = "ON"/>
<cmd id = "161" text = "RETURN"/>
<cmd id = "162" text = "ERASE"/>
<cmd id = "163" text = "CURSOR"/>
<cmd id = "164" text = "IF"/>
<cmd id = "165" text = "RESTORE"/>
<cmd id = "166" text = "SCREEN"/>
<cmd id = "167" text = "COLOR"/>
<cmd id = "168" text = "LINE"/>
<cmd id = "169" text = "SOUND"/>
<cmd id = "170" text = "BEEP"/>
<cmd id = "171" text = "CONSOLE"/>
<cmd id = "172" text = "CLS"/>
<cmd id = "173" text = "OUT"/>
<cmd id = "174" text = "CALL"/>
<cmd id = "175" text = "POKE"/>
<cmd id = "176" text = "PSET"/>
<cmd id = "177" text = "PRESET"/>
<cmd id = "178" text = "PAINT"/>
<cmd id = "179" text = "BLINE"/>
<cmd id = "180" text = "POSITION"/>
<cmd id = "181" text = "HCOPY"/>
<cmd id = "182" text = "SPRITE"/>
<cmd id = "183" text = "PATTERN"/>
<cmd id = "184" text = "CIRCLE"/>
<cmd id = "185" text = "BCIRCLE"/>
<cmd id = "186" text = "MAG"/>
<cmd id = "187" text = "VPOKE"/>
<cmd id = "188" text = "MOTOR"/>
<cmd id = "192" text = "^"/>
<cmd id = "193" text = "*"/>
<cmd id = "194" text = "/"/>
<cmd id = "195" text = "MOD"/>
<cmd id = "196" text = "+"/>
<cmd id = "197" text = "-"/>
<cmd id = "198" text = "&lt;&gt;"/>
<cmd id = "199" text = "&gt;="/>
<cmd id = "200" text = "&lt;="/>
<cmd id = "201" text = "&gt;"/>
<cmd id = "202" text = "&lt;"/>
<cmd id = "203" text = "="/>
<cmd id = "204" text = "NOT"/>
<cmd id = "205" text = "AND"/>
<cmd id = "206" text = "OR"/>
<cmd id = "207" text = "XOR"/>
<cmd id = "224" text = "FN"/>
<cmd id = "225" text = "TO"/>
<cmd id = "226" text = "STEP"/>
<cmd id = "227" text = "THEN"/>
<cmd id = "228" text = "TAB"/>
<cmd id = "229" text = "SPC"/>
</standard>
<special> <!-- These commands are prefixed with 128. eg 8080 = ABS-->
<cmd id = "128" text = "ABS"/>
<cmd id = "129" text = "RND"/>
<cmd id = "130" text = "SIN"/>
<cmd id = "131" text = "COS"/>
<cmd id = "132" text = "TAN"/>
<cmd id = "133" text = "ASN"/>
<cmd id = "134" text = "ACS"/>
<cmd id = "135" text = "ATN"/>
<cmd id = "136" text = "LOG"/>
<cmd id = "137" text = "LGT"/>
<cmd id = "138" text = "LTW"/>
<cmd id = "139" text = "EXP"/>
<cmd id = "140" text = "RAD"/>
<cmd id = "141" text = "DEG"/>
<cmd id = "142" text = "PI"/>
<cmd id = "143" text = "SQR"/>
<cmd id = "144" text = "INT"/>
<cmd id = "145" text = "SGN"/>
<cmd id = "146" text = "ASC"/>
<cmd id = "147" text = "LEN"/>
<cmd id = "148" text = "VAL"/>
<cmd id = "149" text = "PEEK"/>
<cmd id = "150" text = "INP"/>
<cmd id = "151" text = "FRE"/>
<cmd id = "152" text = "VPEEK"/>
<cmd id = "153" text = "STICK"/>
<cmd id = "154" text = "STRIG"/>
<cmd id = "160" text = "CHR$"/>
<cmd id = "161" text = "HEX$"/>
<cmd id = "162" text = "INKEY$"/>
<cmd id = "163" text = "LEFT$"/>
<cmd id = "164" text = "RIGHT$"/>
<cmd id = "165" text = "MID$"/>
<cmd id = "166" text = "STR$"/>
<cmd id = "167" text = "TIME$"/>
</special>
</cmds>

By the way Bimbo72, I used Goldwave too, good tool despite the bugs ;-)
  View user's profile Send private message
  • Joined: 08 Aug 2005
  • Posts: 203
  • Location: Italy
Reply with quote
Post Posted: Thu Feb 02, 2006 10:36 am
Thank you kerrjnr and Maxim,
uhm, let'see.... you're right the first byte seems to be the lenght of the line in byte.
I'm doing this work because i would like to be able to write down BASIC Programs in Ascii text file from PC, and be able to convert it in a final Wav or mp3 file to read from SEGA SC-3000.
so what is needed is to encode, as BASIC interpreter does, the full program in the right sequence of binary digits. that's why the need to set up a command table from where we can take the right value to put in the sequence.
So the list you wrote starts from 130, this means that if the byte the interpreter read after the line number is < 130 is a normal alphanumerical character and if it is greater is a command... if it is a command it checks also if there is a &H80 before to see if it is special command, right ?
Now, if i digit a special graph character in a normal line:
10 [graph+V]
there is an error, but
10 PRINT "[graph+V]"
is ok...
this means that the interpreter check also what command you have typed in and expect something after that....
Right ?
  View user's profile Send private message Visit poster's website
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14745
  • Location: London
Reply with quote
Post Posted: Thu Feb 02, 2006 11:40 am
The interpreter is more likely to be checking if the byte is 128 ($80) or more (checking the sign bit) and then reading an additional byte if the value is exactly 128.

BASIC is checking your code for validity before allowing you to enter it; it can see that you've entered a line where the first byte is not an instruction, which is invalid. You'd have to do a full disassembly to figure out all the validity checks. I expect that you could load this invalid data from tape (bypassing the input checking) but it would give errors when run.
  View user's profile Send private message Visit poster's website
  • Joined: 08 Aug 2005
  • Posts: 203
  • Location: Italy
Reply with quote
Post Posted: Thu Feb 02, 2006 12:19 pm
Uhm, yes... this means that if i want to do a converter from txt to wav i need to develop an exact "copy" of the SEGA BASIC interpreter, so that it can parse the text and save the right sequence of bit in a waveform....
This trip is becoming harder day by day!!! :-)

By the way... in the waveform i've found something i can't really explain... I can imagine it is useful to check when a saved program audio stream starts and when it finish...

[img]http://us.f1.yahoofs.com/bc/5bfe47d_m418a4d1f/bc/I+miei+documenti/START+OF+DATA+STREAM.jpg?bfjlf4DByuQpYrDJ[/img]

[img]http://us.f1.yahoofs.com/bc/5bfe47d_m418a4d1f/bc/I+miei+documenti/END+OF+DATA+STREAM.jpg?bfjlf4DB3PxYSM7T[/img]

It's not encoded as a normal bit but a simple signal...
  View user's profile Send private message Visit poster's website
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14745
  • Location: London
Reply with quote
Post Posted: Thu Feb 02, 2006 12:40 pm
I suggest using a forum-friendly image host like http://imageshack.us, and avoiding direct-linking large images in general. As it is, I can only see the first of the two images after fixing the URLs.

You're right, those signals do not represent valid data so they are used to signal to the reciever when the data being input is valid. It stops the tape loader from producing junk data if the audio is not started and stopped at the right times.

A TXT to WAV converter doesn't have to validate the data - you could add in a few simple validations, like "the first token on a line must be a command", "text must be in quotes", etc - but it's up to the SC-3000 BASIC to gracefully fail on invalid commands so your converter could be quite lax, relying on the user to fix their own bugs. Of course, it'd aid development to be able to catch these kinds of bugs on the PC in 0.001s instead of loading the program on the SC-3000 every time...
  View user's profile Send private message Visit poster's website
  • Joined: 08 Aug 2005
  • Posts: 203
  • Location: Italy
Reply with quote
Post Posted: Thu Feb 02, 2006 1:21 pm
Sorry for the images... i usually don't write in forums... :-)
I will check it. Thank you.

When the converter program will read a BASIC line, must read char by car and convert for example [PRINT] in 145 then add next characters as single chars until he find the : or the CR character.

In this start stage, probably, i will bypass the "error checking" routine, so that i can try the snthesis of the waveform from txt.
Inside the ROM memory area i saw there are all the error messages that the interpreter gives... theese may be helpful to build an error checker.

I was wondering if it can be possible to readdress the save command to the audio and try to dump it in a wav within MEKA... at least to export programs from the emulator....
  View user's profile Send private message Visit poster's website
  • Site Admin
  • Joined: 08 Jul 2001
  • Posts: 8652
  • Location: Paris, France
Reply with quote
Post Posted: Thu Feb 02, 2006 1:32 pm
Bimbo72 wrote
I was wondering if it can be possible to readdress the save command to the audio and try to dump it in a wav within MEKA... at least to export programs from the emulator....

Well that's one of the point of tape emulation. Thanks all for your work (I've still got the stuff Kernnjr, very useful), it'll be done. In the meanwhile you're free to get MEKA WIP sources and do some mods if you need a particular quick hack/feature.

Also in MEKA with debug mode enabled you can dump RAM to a file. Based on a RAM dump you should be able to export the basic code somehow.
  View user's profile Send private message Visit poster's website
  • Joined: 08 Aug 2005
  • Posts: 203
  • Location: Italy
Reply with quote
Post Posted: Thu Feb 02, 2006 2:16 pm
HI, bock...
That was one of the possibilities I checked... but, in the RAM dump i couldn't find the right starting position of the program and the byte structure... I think the tape format is easier than the ram one...
Actually i didn't consider the possibility to try out some more times in decoding RAM dumps. I think I'll make some work on that...
  View user's profile Send private message Visit poster's website
  • Joined: 08 Aug 2005
  • Posts: 203
  • Location: Italy
Reply with quote
Post Posted: Thu Feb 02, 2006 2:42 pm
uhm... just have done some tries... Actually the structure of the programs seems to be the same as the tape....

byte description
1 length in byte of the line exept (CR)
4 line number
n BASIC CODE
1 0D (CR)
...
...
i've found it at &h9800 of RAM... but i still don't know the meaning of this position... if it is dynamic or not...
  View user's profile Send private message Visit poster's website
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14745
  • Location: London
Reply with quote
Post Posted: Thu Feb 02, 2006 2:59 pm
It may differ between BASIC versions. To allow for different sizes of RAM, it's possible that the program is stored at the "top" after all of the other memory needed for BASIC is allocated. Thus, it seems the version you're using uses $1800 (6144) bytes for the interpreter and leaves what's left of the RAM for the program - with 32KB of on-cart RAM, you'd get $6800 (26624) bytes free, which is surprisingly close to the 26620 that Basic Level 3 reports.
  View user's profile Send private message Visit poster's website
  • Joined: 08 Aug 2005
  • Posts: 203
  • Location: Italy
Reply with quote
Post Posted: Thu Feb 02, 2006 3:26 pm
Thank you, this is helpful... maybe it would be possible to create an application that reads the RAM dump and convert it in a TXT at the same way but without the wav conversion step. The problem would be to import from a txt file to SC-3000... the only port is the cassette in plug.

Maybe a particular hardware plugged to the joystick port can translate byte to pulses... And some BASIC program can put it into memory at the right location... sorry for the crazy idea... I'm tired :-)
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jun 2005
  • Posts: 42
  • Location: Wairarapa, New Zealand
Reply with quote
Post Posted: Thu Feb 02, 2006 5:20 pm
Hey there guys, I hate to be a downer but all this analysis and discussion has already been done - honestly, just list the questions and I'll answer them, I haven't been able to keep up with you since my last post because of the tmie dif. here in NZ (I've just woken up) but just give me a chance to write the document I mentioned then I'll send it to you directly.

Like you I was all fire and brimstone when I first started doing exactly what you're doing but when it comes to modifying MEKA, even though it's possible, I've chosen to leave it to the expert - Bock's got a couple of great ideas for implementing cassette emulation so it might be best to be patient and let him do it (not that I'm trying to put out your fire - if you think you can do it then go for your life!).

Also I've been writing C#, vb6 and vb.net SEGA BASIC editors that will save to wave for loading on the SEGA in the meantime, I'll release it when I'm happy with what I've done.

Have to go to work now and the IRB Sevens is on in Wellington today and tomorrow so don't hold your breath for another posting for a couple of days! <grin>
  View user's profile Send private message
  • Joined: 08 Aug 2005
  • Posts: 203
  • Location: Italy
Reply with quote
Post Posted: Thu Feb 02, 2006 8:05 pm
Yes, You're right... maybe some discussion has already been made, but you know... the passion for this kind of things is too much after you discover something :-) I thought to post what i discovered to give help to anyone who would like to learn , research and so on...
Anyway... i will be patient to see some app.
Thank you.
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jun 2005
  • Posts: 42
  • Location: Wairarapa, New Zealand
Reply with quote
Post Posted: Fri Feb 03, 2006 1:45 am
So Bock, any idea when we can expect you to update MEKA to handle cassette dumps? :-) No pressure...
  View user's profile Send private message
  • Joined: 22 Apr 2018
  • Posts: 530
Reply with quote
Post Posted: Thu Jan 11, 2024 4:10 am
Last edited by bsittler on Fri Jan 12, 2024 3:18 am; edited 5 times in total
SC-3000 tapes use the same low-level encoding as MSX ones, and also the same baud rate. Thanks to this, most of the usual MSX tools work fine for processing them, including converting them to and from CAS format (for BIOS-only tapes) or TSX format (for all tapes).

MSX-compatible CAS format is a great representation for them when applicable because it allows simple file checksums to be used to check for identical tape contents / "good rips" of tapes, and it is also very compact and yet readable, with 1 byte of CAS being one encoded file or header byte on the tape.

Of course the type code bytes for file header blocks, file data blocks, etc. do differ between SC-3000 and MSX.

I needed to convert some tokenized BASIC from a file extracted from a CAS conversion of an SC-3000 file to ASCII for analysis but couldn't find a ready-made tool that did this, so I wrote one in (admittedly terrible) Python 3 using the information from this thread that converts tokenized BASIC bytes read from /dev/fd/0 to ASCII BASIC written out to /dev/fd/1
#!/usr/bin/env python3

# Converts SC-3000 tokenized BASIC from /dev/fd/0 to ASCII written to /dev/fd/1

import struct

def num2asc(num):
 ret, = struct.unpack('<L', bytes(tuple(num)))
 return str(ret)

def scbas2asc(byts, ofd):
 decoder = {
  (130): b"LIST",
  (131): b"LLIST",
  (132): b"AUTO",
  (133): b"DELETE",
  (134): b"RUN",
  (135): b"CONT",
  (136): b"LOAD",
  (137): b"SAVE",
  (138): b"VERIFY",
  (139): b"NEW",
  (140): b"RENUM",
  (144): b"REM",
  (145): b"PRINT",
  (146): b"LPRINT",
  (147): b"DATA",
  (148): b"DEF",
  (149): b"INPUT",
  (150): b"READ",
  (151): b"STOP",
  (152): b"END",
  (153): b"LET",
  (154): b"DIM",
  (155): b"FOR",
  (156): b"NEXT",
  (157): b"GOTO",
  (158): b"GOSUB",
  (159): b"GO",
  (160): b"ON",
  (161): b"RETURN",
  (162): b"ERASE",
  (163): b"CURSOR",
  (164): b"IF",
  (165): b"RESTORE",
  (166): b"SCREEN",
  (167): b"COLOR",
  (168): b"LINE",
  (169): b"SOUND",
  (170): b"BEEP",
  (171): b"CONSOLE",
  (172): b"CLS",
  (173): b"OUT",
  (174): b"CALL",
  (175): b"POKE",
  (176): b"PSET",
  (177): b"PRESET",
  (178): b"PAINT",
  (179): b"BLINE",
  (180): b"POSITION",
  (181): b"HCOPY",
  (182): b"SPRITE",
  (183): b"PATTERN",
  (184): b"CIRCLE",
  (185): b"BCIRCLE",
  (186): b"MAG",
  (187): b"VPOKE",
  (188): b"MOTOR",
  (192): b"^",
  (193): b"*",
  (194): b"/",
  (195): b"MOD",
  (196): b"+",
  (197): b"-",
  (198): b"<>",
  (199): b">=",
  (200): b"<=",
  (201): b">",
  (202): b"<",
  (203): b"=",
  (204): b"NOT",
  (205): b"AND",
  (206): b"OR",
  (207): b"XOR",
  (224): b"FN",
  (225): b"TO",
  (226): b"STEP",
  (227): b"THEN",
  (228): b"TAB",
  (229): b"SPC",
  (128, 128): b"ABS",
  (128, 129): b"RND",
  (128, 130): b"SIN",
  (128, 131): b"COS",
  (128, 132): b"TAN",
  (128, 133): b"ASN",
  (128, 134): b"ACS",
  (128, 135): b"ATN",
  (128, 136): b"LOG",
  (128, 137): b"LGT",
  (128, 138): b"LTW",
  (128, 139): b"EXP",
  (128, 140): b"RAD",
  (128, 141): b"DEG",
  (128, 142): b"PI",
  (128, 143): b"SQR",
  (128, 144): b"INT",
  (128, 145): b"SGN",
  (128, 146): b"ASC",
  (128, 147): b"LEN",
  (128, 148): b"VAL",
  (128, 149): b"PEEK",
  (128, 150): b"INP",
  (128, 151): b"FRE",
  (128, 152): b"VPEEK",
  (128, 153): b"STICK",
  (128, 154): b"STRIG",
  (128, 160): b"CHR$",
  (128, 161): b"HEX$",
  (128, 162): b"INKEY$",
  (128, 163): b"LEFT$",
  (128, 164): b"RIGHT$",
  (128, 165): b"MID$",
  (128, 166): b"STR$",
  (128, 167): b"TIME$",
 }
 esc = None
 in_quotes = False
 in_rem = False
 in_data = False
 lnum = []
 llen = None
 byt0 = None
 csum = 0
 for byt in byts:
  if byt0 is None:
    byt0 = byt
    assert byt0 == 0x17 # no idea why, but SC-3000 BASIC III starts with this byte
    continue
  csum += byt
  csum &= 0xFF
  if llen is None:
    llen = byt
    continue
  if len(lnum) < 4:
    lnum.append(byt)
    if len(lnum) == 4:
     ofd.write((num2asc(lnum) + ' ').encode('iso-8859-1'))
    continue
  llen -= 1
  if esc is not None:
    byt = (esc, byt)
    esc = None
  elif (byt == 128) and not (in_quotes or in_rem or in_data):
    esc = byt
    continue
  tok = None
  if not (in_quotes or in_rem or in_data):
   tok = decoder.get(byt)
  if tok is None:
   tok = bytes((byt,))
  if tok == b"REM":
   in_rem = True
  if tok == b"DATA":
   in_data = True
  if (tok == b':') and in_data and not in_quotes:
   in_data = False
  if (tok == b'"') and not in_rem:
   in_quotes = not in_quotes
  if tok == b'\r':
    ofd.write(b'\r\n')
    in_quotes = False
    in_rem = False
    in_data = False
    assert llen == -1, llen
    lnum = []
    llen = None
    continue
  ofd.write(tok)
 if llen is not None:
  if llen == 0 and len(lnum) < 4:
    assert csum == 0, 'ERROR: Checksum should always reach zero but it was: 0x%02X' % csum
    if len(lnum) > 1:
     assert lnum[1] == 0, 'ERROR: First pad byte should be zero but is was: 0x%02X' % lnum[1]
    if len(lnum) > 2:
     assert lnum[2] == 0, 'ERROR: Second pad byte should be zero but is was: 0x%02X' % lnum[2]
  else:
    assert llen == -1, llen
    assert False, 'ERROR: Missing end of program marker'

scbas2asc(open("/dev/fd/0", "rb").read(), open("/dev/fd/1", "wb"))

also entirely possible special bytes inside string literals or comments are totally broken by my lister

edit: updated to not expand tokens inside quoted strings

edit 2: also in remarks! added test files for the string/remark embedding cases

edit 3: also in data statements! added a test for that too
character_test.png (442.19 KB)
character_test.png
character_test2.png (273.4 KB)
character_test2.png
character_test3.png (295.41 KB)
character_test3.png
characters_tests.zip (1017.61 KB)
scbas2asc.py and character tests

  View user's profile Send private message
  • Joined: 22 Apr 2018
  • Posts: 530
Reply with quote
Post Posted: Thu Jan 11, 2024 4:31 am
(if only MAME implemented the printer I could have just used LLIST of course!)
  View user's profile Send private message
  • Joined: 22 Apr 2018
  • Posts: 530
Reply with quote
Post Posted: Thu Jan 11, 2024 6:33 am
Here is one more tool I wrote, this one "explodes" an MSX or SC-3000 CAS file into its blocks/files. Right now it doesn't understand SC-3000 file header or data blocks specifically, but already it proved useful when analyzing SC-3000 tape contents.

And I am sorry, it is a Python 3 one-liner encapsulated as a Bash script as I literally just exported it from my shell history

#!/bin/bash --

python3 -c 'import struct,os.path,re,sys;_,casfile=sys.argv;cas=open(casfile,"rb").read();hdr=b"\x1f\xa6\xde\xba\xcc\x13\x7d\x74";assert cas[:len(hdr)]==hdr;chunks=cas[len(hdr):].split(hdr);chunks=hdr.join([((chunk[:21] + hdr + chunk[21:]) if (len(chunk) > 21 and chunk[:10]==chunk[:1]*10 and chunk[:1] in b"\xd0\xd3\xea" and chunk[16:21]==b"\x00"*5) else chunk) for chunk in chunks]).split(hdr);names=([((chunk[:10]==chunk[:1]*10 and chunk[:1] in b"\xd0\xd3\xea") and (chunk[10:16].decode("iso-8859-1").rstrip(" ") + {b"\xd0":".BIN",b"\xd3":".BAS",b"\xea":".TXT"}[chunk[:1]]) or (len(chunk) in (23, 24) and chunk[:3] == b"\xab\xcd\xef" and (chunk[3:11].rstrip(b" ")+b"."+chunk[11:14].rstrip(b" ")).decode("iso-8859-1")) or "CHUNK.DAT") for (i, chunk) in enumerate(chunks)]);'$'\n''for (i,chunk) in enumerate(chunks): fn=("%s_%02d_%s" % (re.sub(r"(?i)[.]cas$", "", os.path.split(casfile)[1] or "cas"),1+i,((names[i] == "CHUNK.DAT") and i and (names[i-1] != "CHUNK.DAT") and names[i-1]) or (names[i] != "CHUNK.DAT" and (names[i] + "_hdr")) or names[i]));chunk=chunks[i];fn,chunk=(i and chunks[i-1][:1]==b"\xd0" and fn.endswith(".BIN") and (("%s_%04X_%04X_%04X" % (fn,*struct.unpack("<HHH",chunk[:6])), chunk[6:]))) or (fn, chunk);print(fn);assert not os.path.exists(fn),"%s already exists" % fn;open(fn,"wb").write(chunk)' "$@"
exit $?
  View user's profile Send private message
  • Joined: 22 Apr 2018
  • Posts: 530
Reply with quote
Post Posted: Fri Jan 12, 2024 3:40 am
Yet another hacky filter, this one transforms the region-independent parts of the SC-3000 character set from stdin to Unicode on stdout... well, actually it does the rest too but leaves them in the Private Use Area
python3 -c 'ifd=open("/dev/fd/0","rb");ofd=open("/dev/fd/1","w");charmap={0x5c:"\u00a5",0x5f:"\u03c0",0x80:"\u2502",0x81:"\u2500",
0x82:"\u2534",0x83:"\u252c",0x84:"\u2524",0x85:"\u251c",
0x86:"\u250c",0x87:"\u2514",0x88:"\u2510",0x89:"\u2518",
0x8a:"\u256d",0x8b:"\u2570",0x8c:"\u256e",0x8d:"\u256f",
0x8e:"\u2191",0x8f:"\u2190",0x90:"\u2592",0x91:"\u2573",
0x92:"\u253c",0x93:"\u2571",0x94:"\u2572",0x95:"\u25e2",
0x96:"\u25e3",0x97:"\u25e5",0x98:"\u25e4",0x99:"\u2581",
0x9a:"\u2582",0x9b:"\u2584",0x9c:"\u2580",0x9d:"\U0001fb82",
0x9e:"\u2594",0x9f:"\u258f",0xe0:"\u258e",0xe1:"\u258c",
0xe2:"\u2590",0xe3:"\U0001fb87",0xe4:"\u2595",0xe5:"\u2588",
0xea:"\u259e",0xeb:"\u25cb",0xec:"\u25cf",0xf5:"\u2660",
0xf6:"\u2665",0xf7:"\u2666",0xf8:"\u2663",0xfd:"\U0001fbc5",
0xfe:"\xf7"};[ofd.write(charmap.get(byt,chr(byt) if byt<0x80 else chr(byt | 0xf800))) for byt in ifd.read()]'
  View user's profile Send private message
  • Joined: 22 Apr 2018
  • Posts: 530
Reply with quote
Post Posted: Fri Jan 12, 2024 9:28 am
apparently for sc-3000 other people already noticed the MSX CAS compatibility and made tools to use that. i only realized now https://web.archive.org/web/20190213073250/http://www.geocities.jp:80/parallel_c... (archived, geocities site is down permanently and i have not found another more live replacement) - however it uses the older incompatible MSX CAS format that is basically no longer used

someone even made a tool to go straight from WAV to ASCII BASIC! http://68000.web.fc2.com/wav2basic/src/wav2basic.c
  View user's profile Send private message
  • Joined: 25 Feb 2013
  • Posts: 384
  • Location: Osaka
Reply with quote
Post Posted: Sun Jan 14, 2024 8:28 am
bsittler wrote
SC-3000 tapes use the same low-level encoding as MSX ones, and also the same baud rate. Thanks to this, most of the usual MSX tools work fine for processing them, including converting them to and from CAS format (for BIOS-only tapes) or TSX format (for all tapes).

MSX-compatible CAS format is a great representation for them when applicable because it allows simple file checksums to be used to check for identical tape contents / "good rips" of tapes, and it is also very compact and yet readable, with 1 byte of CAS being one encoded file or header byte on the tape.

Of course the type code bytes for file header blocks, file data blocks, etc. do differ between SC-3000 and MSX.

I needed to convert some tokenized BASIC from a file extracted from a CAS conversion of an SC-3000 file to ASCII for analysis but couldn't find a ready-made tool that did this, so I wrote one in (admittedly terrible) Python 3 using the information from this thread that converts tokenized BASIC bytes read from /dev/fd/0 to ASCII BASIC written out to /dev/fd/1
#!/usr/bin/env python3

# Converts SC-3000 tokenized BASIC from /dev/fd/0 to ASCII written to /dev/fd/1

import struct

def num2asc(num):
 ret, = struct.unpack('<L', bytes(tuple(num)))
 return str(ret)

def scbas2asc(byts, ofd):
 decoder = {
  (130): b"LIST",
  (131): b"LLIST",
  (132): b"AUTO",
  (133): b"DELETE",
  (134): b"RUN",
  (135): b"CONT",
  (136): b"LOAD",
  (137): b"SAVE",
  (138): b"VERIFY",
  (139): b"NEW",
  (140): b"RENUM",
  (144): b"REM",
  (145): b"PRINT",
  (146): b"LPRINT",
  (147): b"DATA",
  (148): b"DEF",
  (149): b"INPUT",
  (150): b"READ",
  (151): b"STOP",
  (152): b"END",
  (153): b"LET",
  (154): b"DIM",
  (155): b"FOR",
  (156): b"NEXT",
  (157): b"GOTO",
  (158): b"GOSUB",
  (159): b"GO",
  (160): b"ON",
  (161): b"RETURN",
  (162): b"ERASE",
  (163): b"CURSOR",
  (164): b"IF",
  (165): b"RESTORE",
  (166): b"SCREEN",
  (167): b"COLOR",
  (168): b"LINE",
  (169): b"SOUND",
  (170): b"BEEP",
  (171): b"CONSOLE",
  (172): b"CLS",
  (173): b"OUT",
  (174): b"CALL",
  (175): b"POKE",
  (176): b"PSET",
  (177): b"PRESET",
  (178): b"PAINT",
  (179): b"BLINE",
  (180): b"POSITION",
  (181): b"HCOPY",
  (182): b"SPRITE",
  (183): b"PATTERN",
  (184): b"CIRCLE",
  (185): b"BCIRCLE",
  (186): b"MAG",
  (187): b"VPOKE",
  (188): b"MOTOR",
  (192): b"^",
  (193): b"*",
  (194): b"/",
  (195): b"MOD",
  (196): b"+",
  (197): b"-",
  (198): b"<>",
  (199): b">=",
  (200): b"<=",
  (201): b">",
  (202): b"<",
  (203): b"=",
  (204): b"NOT",
  (205): b"AND",
  (206): b"OR",
  (207): b"XOR",
  (224): b"FN",
  (225): b"TO",
  (226): b"STEP",
  (227): b"THEN",
  (228): b"TAB",
  (229): b"SPC",
  (128, 128): b"ABS",
  (128, 129): b"RND",
  (128, 130): b"SIN",
  (128, 131): b"COS",
  (128, 132): b"TAN",
  (128, 133): b"ASN",
  (128, 134): b"ACS",
  (128, 135): b"ATN",
  (128, 136): b"LOG",
  (128, 137): b"LGT",
  (128, 138): b"LTW",
  (128, 139): b"EXP",
  (128, 140): b"RAD",
  (128, 141): b"DEG",
  (128, 142): b"PI",
  (128, 143): b"SQR",
  (128, 144): b"INT",
  (128, 145): b"SGN",
  (128, 146): b"ASC",
  (128, 147): b"LEN",
  (128, 148): b"VAL",
  (128, 149): b"PEEK",
  (128, 150): b"INP",
  (128, 151): b"FRE",
  (128, 152): b"VPEEK",
  (128, 153): b"STICK",
  (128, 154): b"STRIG",
  (128, 160): b"CHR$",
  (128, 161): b"HEX$",
  (128, 162): b"INKEY$",
  (128, 163): b"LEFT$",
  (128, 164): b"RIGHT$",
  (128, 165): b"MID$",
  (128, 166): b"STR$",
  (128, 167): b"TIME$",
 }
 esc = None
 in_quotes = False
 in_rem = False
 in_data = False
 lnum = []
 llen = None
 byt0 = None
 csum = 0
 for byt in byts:
  if byt0 is None:
    byt0 = byt
    assert byt0 == 0x17 # no idea why, but SC-3000 BASIC III starts with this byte
    continue
  csum += byt
  csum &= 0xFF
  if llen is None:
    llen = byt
    continue
  if len(lnum) < 4:
    lnum.append(byt)
    if len(lnum) == 4:
     ofd.write((num2asc(lnum) + ' ').encode('iso-8859-1'))
    continue
  llen -= 1
  if esc is not None:
    byt = (esc, byt)
    esc = None
  elif (byt == 128) and not (in_quotes or in_rem or in_data):
    esc = byt
    continue
  tok = None
  if not (in_quotes or in_rem or in_data):
   tok = decoder.get(byt)
  if tok is None:
   tok = bytes((byt,))
  if tok == b"REM":
   in_rem = True
  if tok == b"DATA":
   in_data = True
  if (tok == b':') and in_data and not in_quotes:
   in_data = False
  if (tok == b'"') and not in_rem:
   in_quotes = not in_quotes
  if tok == b'\r':
    ofd.write(b'\r\n')
    in_quotes = False
    in_rem = False
    in_data = False
    assert llen == -1, llen
    lnum = []
    llen = None
    continue
  ofd.write(tok)
 if llen is not None:
  if llen == 0 and len(lnum) < 4:
    assert csum == 0, 'ERROR: Checksum should always reach zero but it was: 0x%02X' % csum
    if len(lnum) > 1:
     assert lnum[1] == 0, 'ERROR: First pad byte should be zero but is was: 0x%02X' % lnum[1]
    if len(lnum) > 2:
     assert lnum[2] == 0, 'ERROR: Second pad byte should be zero but is was: 0x%02X' % lnum[2]
  else:
    assert llen == -1, llen
    assert False, 'ERROR: Missing end of program marker'

scbas2asc(open("/dev/fd/0", "rb").read(), open("/dev/fd/1", "wb"))

also entirely possible special bytes inside string literals or comments are totally broken by my lister

edit: updated to not expand tokens inside quoted strings

edit 2: also in remarks! added test files for the string/remark embedding cases

edit 3: also in data statements! added a test for that too


You may be interested in
https://github.com/fabiodl/sctape
and
https://github.com/fabiodl/mame/commit/f6e41bef9226c234179ee3dfac5ee630739b1f4f
and
https://sc3000.neocities.org/
  View user's profile Send private message
  • Joined: 18 Jun 2005
  • Posts: 42
  • Location: Wairarapa, New Zealand
Reply with quote
Updated SC-3000 tools
Post Posted: Sat Jan 27, 2024 11:58 pm
FYI Below is a link to my Public "SEGA Survivors" OneDrive folder, containing the latest versions of a couple of old tools I developed - SC-3000 WAV Writer x64 v3.1.5.msi and sega-tile-detector-setup-v1_9.msi.

I had reason to use them for some redesign of a SC-3000 machine-code RPG game I'm developing and needed to tweak a couple of things to get them working, I did the bare minimum for time but will probably look at rewriting them in Python in future, if there's any need. In the meantime, they're Windows Desktop apps, requiring .NET FW v4.8.

https://1drv.ms/f/s!Ap5vPj4tPrsiikjSJIKXeIdnNuW0?e=gktgu2

Regards
  View user's profile Send private message
Reply to topic



Back to the top of this page

Back to SMS Power!