Add BASIC import (#12)
* Add BASIC import * Fix text to BASIC when line feed after token
This commit is contained in:
parent
a649647739
commit
48aa7a9331
|
@ -16,7 +16,7 @@ There are binaries here:
|
||||||
## Current TODO list
|
## Current TODO list
|
||||||
1. Allow > 128 KB file support
|
1. Allow > 128 KB file support
|
||||||
2. Create/Delete directories
|
2. Create/Delete directories
|
||||||
3. Add file/directory/basic tests
|
3. Add file/directory tests
|
||||||
4. Add rename
|
4. Add rename
|
||||||
5. Add in-place file/directory moves
|
5. Add in-place file/directory moves
|
||||||
|
|
||||||
|
|
14
main.go
14
main.go
|
@ -39,7 +39,7 @@ func main() {
|
||||||
flag.IntVar(&volumeSize, "s", 65535, "Number of blocks to create the volume with (default 65535, 64 to 65535, 0x0040 to 0xFFFF hex input accepted)")
|
flag.IntVar(&volumeSize, "s", 65535, "Number of blocks to create the volume with (default 65535, 64 to 65535, 0x0040 to 0xFFFF hex input accepted)")
|
||||||
flag.StringVar(&volumeName, "v", "NO.NAME", "Specifiy a name for the volume from 1 to 15 characters")
|
flag.StringVar(&volumeName, "v", "NO.NAME", "Specifiy a name for the volume from 1 to 15 characters")
|
||||||
flag.IntVar(&blockNumber, "b", 0, "A block number to read/write from 0 to 65535 (0x0000 to 0xFFFF hex input accepted)")
|
flag.IntVar(&blockNumber, "b", 0, "A block number to read/write from 0 to 65535 (0x0000 to 0xFFFF hex input accepted)")
|
||||||
flag.IntVar(&fileType, "t", 6, "ProDOS FileType: 0x04 for TXT, 0x06 for BIN, 0xFA for BAS, 0xFF for SYS etc.")
|
flag.IntVar(&fileType, "t", 6, "ProDOS FileType: 0x04 for TXT, 0x06 for BIN, 0xFC for BAS, 0xFF for SYS etc.")
|
||||||
flag.IntVar(&auxType, "a", 0x2000, "ProDOS AuxType from 0 to 65535 (0x0000 to 0xFFFF hex input accepted)")
|
flag.IntVar(&auxType, "a", 0x2000, "ProDOS AuxType from 0 to 65535 (0x0000 to 0xFFFF hex input accepted)")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
@ -117,9 +117,15 @@ func main() {
|
||||||
fmt.Printf("Failed to open input file %s: %s", inFileName, err)
|
fmt.Printf("Failed to open input file %s: %s", inFileName, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
if strings.HasSuffix(strings.ToLower(inFileName), ".bas") {
|
||||||
|
inFile, err = prodos.ConvertTextToBasic(string(inFile))
|
||||||
|
fileType = 0xFC
|
||||||
|
auxType = 0x0801
|
||||||
|
}
|
||||||
|
|
||||||
// Check for an AppleSingle file as produced by cc65
|
// Check for an AppleSingle file as produced by cc65
|
||||||
if // Magic number
|
if // Magic number
|
||||||
binary.BigEndian.Uint32(inFile[0x00:]) == 0x00051600 &&
|
binary.BigEndian.Uint32(inFile[0x00:]) == 0x00051600 &&
|
||||||
// Version number
|
// Version number
|
||||||
binary.BigEndian.Uint32(inFile[0x04:]) == 0x00020000 &&
|
binary.BigEndian.Uint32(inFile[0x04:]) == 0x00020000 &&
|
||||||
// Number of entries
|
// Number of entries
|
||||||
|
@ -129,7 +135,7 @@ func main() {
|
||||||
// Offset
|
// Offset
|
||||||
binary.BigEndian.Uint32(inFile[0x1E:]) == 0x0000003A &&
|
binary.BigEndian.Uint32(inFile[0x1E:]) == 0x0000003A &&
|
||||||
// Length
|
// Length
|
||||||
binary.BigEndian.Uint32(inFile[0x22:]) == uint32(len(inFile)) - 0x3A &&
|
binary.BigEndian.Uint32(inFile[0x22:]) == uint32(len(inFile))-0x3A &&
|
||||||
// ProDOS File Info ID
|
// ProDOS File Info ID
|
||||||
binary.BigEndian.Uint32(inFile[0x26:]) == 0x0000000B &&
|
binary.BigEndian.Uint32(inFile[0x26:]) == 0x0000000B &&
|
||||||
// Offset
|
// Offset
|
||||||
|
|
193
prodos/basic.go
193
prodos/basic.go
|
@ -7,7 +7,10 @@
|
||||||
package prodos
|
package prodos
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -88,7 +91,7 @@ var tokens = map[byte]string{
|
||||||
0xC9: "-",
|
0xC9: "-",
|
||||||
0xCA: "*",
|
0xCA: "*",
|
||||||
0xCB: "/",
|
0xCB: "/",
|
||||||
0xCC: ";",
|
//0xCC: ";", // fails if this is there
|
||||||
0xCD: "AND",
|
0xCD: "AND",
|
||||||
0xCE: "OR",
|
0xCE: "OR",
|
||||||
0xCF: ">",
|
0xCF: ">",
|
||||||
|
@ -161,95 +164,123 @@ func ConvertBasicToText(basic []byte) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// func ConvertTextToBasic(text string) ([]byte, error) {
|
func ConvertTextToBasic(text string) ([]byte, error) {
|
||||||
// // convert line endings
|
// convert line endings
|
||||||
// text = strings.Replace(text, "\r\n", "\n", -1)
|
text = strings.Replace(text, "\r\n", "\n", -1)
|
||||||
// text = strings.Replace(text, "\r", "\n", -1)
|
text = strings.Replace(text, "\r", "\n", -1)
|
||||||
|
|
||||||
// const startState = 0
|
starting := true
|
||||||
// const lineNumberState = 1
|
parsingLineNumber := false
|
||||||
// const tokenCheckState = 2
|
parsingData := false
|
||||||
// const literalState = 3
|
parsingString := false
|
||||||
// const stringState = 4
|
parsingRem := false
|
||||||
// const dataState = 5
|
foundToken := false
|
||||||
// const endOfLineState = 6
|
|
||||||
|
|
||||||
// state := startState
|
currentByte := 0x0801
|
||||||
|
var lineNumberString string
|
||||||
|
|
||||||
// currentByte := 0x0801
|
basicFile := new(bytes.Buffer)
|
||||||
// var lineNumberString string
|
basicLine := new(bytes.Buffer)
|
||||||
// var tokenString string
|
|
||||||
|
|
||||||
// basicFile := new(bytes.Buffer)
|
skipChars := 0
|
||||||
// basicLine := new(bytes.Buffer)
|
|
||||||
|
|
||||||
// // parse character by character
|
// parse character by character
|
||||||
// for _, c := range text {
|
for index, c := range text {
|
||||||
|
|
||||||
// // skip initial whitespace and look for the start of a line number
|
// skip initial whitespace and look for the start of a line number
|
||||||
// if state == startState {
|
if starting {
|
||||||
// if c == ' ' {
|
if c == ' ' {
|
||||||
// continue
|
continue
|
||||||
// }
|
}
|
||||||
// if c >= '0' && c <= '9' {
|
if c >= '0' && c <= '9' {
|
||||||
// state = lineNumberState
|
starting = false
|
||||||
// } else {
|
parsingLineNumber = true
|
||||||
// return nil, errors.New("unexpected character before line number")
|
} else {
|
||||||
// }
|
return nil, errors.New("unexpected character before line number")
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// // parse line number
|
if skipChars > 0 && c != '\n' {
|
||||||
// if state == lineNumberState {
|
skipChars--
|
||||||
// if c >= '0' && c <= '9' {
|
continue
|
||||||
// lineNumberString += string(c)
|
}
|
||||||
// } else {
|
|
||||||
// lineNumber, err := strconv.ParseUint(lineNumberString, 10, 16)
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// basicLine.WriteByte(byte(lineNumber % 256)) // low byte
|
|
||||||
// basicLine.WriteByte(byte(lineNumber / 256)) // high byte
|
|
||||||
// tokenString = ""
|
|
||||||
// tokenByte = 0
|
|
||||||
// state = tokenCheckState
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if state == tokenCheckState {
|
// parse line number
|
||||||
// // skip initial whitespace
|
if parsingLineNumber {
|
||||||
// if c == ' ' && len(tokenString) == 0 {
|
if c >= '0' && c <= '9' {
|
||||||
// continue
|
lineNumberString += string(c)
|
||||||
// }
|
} else {
|
||||||
|
lineNumber, err := strconv.ParseUint(lineNumberString, 10, 16)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
basicLine.WriteByte(byte(lineNumber % 256)) // low byte
|
||||||
|
basicLine.WriteByte(byte(lineNumber / 256)) // high byte
|
||||||
|
parsingLineNumber = false
|
||||||
|
lineNumberString = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// // finish parsing token if
|
if !parsingLineNumber {
|
||||||
// if c == '\n' {
|
if c == '\n' {
|
||||||
// state = endOfLineState
|
starting = true
|
||||||
// } else if c == '"' {
|
parsingLineNumber = false
|
||||||
// state = stringState
|
parsingData = false
|
||||||
// }
|
parsingRem = false
|
||||||
|
parsingString = false
|
||||||
|
foundToken = false
|
||||||
|
currentByte += basicLine.Len()
|
||||||
|
currentByte += 3
|
||||||
|
// write address of next line
|
||||||
|
basicFile.WriteByte(byte(currentByte % 256))
|
||||||
|
basicFile.WriteByte(byte(currentByte / 256))
|
||||||
|
// write the line
|
||||||
|
basicFile.Write(basicLine.Bytes())
|
||||||
|
basicFile.WriteByte(0x00)
|
||||||
|
basicLine.Reset()
|
||||||
|
} else if parsingData {
|
||||||
|
basicLine.WriteByte(byte(c))
|
||||||
|
} else if parsingRem {
|
||||||
|
basicLine.WriteByte(byte(c))
|
||||||
|
} else if parsingString {
|
||||||
|
basicLine.WriteByte(byte(c))
|
||||||
|
if c == '"' {
|
||||||
|
parsingString = false
|
||||||
|
}
|
||||||
|
} else if c == '"' {
|
||||||
|
parsingString = true
|
||||||
|
basicLine.WriteByte(byte(c))
|
||||||
|
} else {
|
||||||
|
if c == ' ' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// }
|
for key, token := range tokens {
|
||||||
// }
|
if index < len(text)-len(token) {
|
||||||
|
if text[index:index+len(token)] == token {
|
||||||
|
basicLine.WriteByte(byte(key))
|
||||||
|
skipChars = len(token)
|
||||||
|
foundToken = true
|
||||||
|
if key == 0x83 {
|
||||||
|
parsingData = true
|
||||||
|
}
|
||||||
|
if key == 0xB2 {
|
||||||
|
parsingRem = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// return basicFile.Bytes(), nil
|
if foundToken {
|
||||||
// }
|
foundToken = false
|
||||||
|
} else {
|
||||||
|
basicLine.WriteByte(byte(c))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
basicFile.WriteByte(0x00)
|
||||||
|
basicFile.WriteByte(0x00)
|
||||||
|
|
||||||
// func writeTokenOrBytes(parseString string, basicBytes []byte) bool {
|
return basicFile.Bytes(), nil
|
||||||
// if len(parseString) == 0 {
|
}
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
|
|
||||||
// upperToken := strings.ToUpper(parseString)
|
|
||||||
|
|
||||||
// for tokenByte, token := range tokens {
|
|
||||||
// if upperToken == token {
|
|
||||||
// return tokenByte
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if tokenByte > 0 {
|
|
||||||
// basicBytes.WriteByte(tokenByte)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return 0
|
|
||||||
// }
|
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
package prodos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConvertBasicToText(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
basic []byte
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Simple",
|
||||||
|
[]byte{
|
||||||
|
0x14, 0x08, 0x0A, 0x00, 0xBA, 0x22, 0x48, 0x45, 0x4C, 0x4C, 0x4F, 0x20, 0x57, 0x4F, 0x52, 0x4C, 0x44, 0x22, 0x00,
|
||||||
|
0x1A, 0x08, 0x14, 0x00, 0x80, 0x00,
|
||||||
|
0x00, 0x00},
|
||||||
|
"10 PRINT \"HELLO WORLD\"\n20 END \n"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
testname := tt.name
|
||||||
|
t.Run(testname, func(t *testing.T) {
|
||||||
|
text := ConvertBasicToText(tt.basic)
|
||||||
|
if text != tt.want {
|
||||||
|
t.Errorf("%s\ngot '%#v'\nwant '%#v'\n", testname, []byte(text), []byte(tt.want))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertTextToBasic(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
basicText string
|
||||||
|
want []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Hello world",
|
||||||
|
"10 PRINT \"HELLO WORLD\"\n20 END \n",
|
||||||
|
[]byte{
|
||||||
|
0x14, 0x08, 0x0A, 0x00, 0xBA, 0x22, 0x48, 0x45, 0x4C, 0x4C, 0x4F, 0x20, 0x57, 0x4F, 0x52, 0x4C, 0x44, 0x22, 0x00,
|
||||||
|
0x1A, 0x08, 0x14, 0x00, 0x80, 0x00,
|
||||||
|
0x00, 0x00}},
|
||||||
|
{
|
||||||
|
"Variables",
|
||||||
|
"10 A = 1\n",
|
||||||
|
[]byte{
|
||||||
|
0x09, 0x08, 0x0A, 0x00, 0x41, 0xD0, 0x31, 0x00,
|
||||||
|
0x00, 0x00}},
|
||||||
|
{
|
||||||
|
"Rem",
|
||||||
|
"10 REM x y z\n",
|
||||||
|
[]byte{
|
||||||
|
0x0C, 0x08, 0x0A, 0x00, 0xB2, 0x78, 0x20, 0x79, 0x20, 0x7A, 0x00,
|
||||||
|
0x00, 0x00}},
|
||||||
|
{
|
||||||
|
"Punctuation",
|
||||||
|
"1 PRINT ;: PRINT\n",
|
||||||
|
[]byte{
|
||||||
|
0x0A, 0x08, 0x01, 0x00, 0xBA, 0x3B, 0x3A, 0xBA, 0x00,
|
||||||
|
0x00, 0x00}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
testname := tt.name
|
||||||
|
t.Run(testname, func(t *testing.T) {
|
||||||
|
basic, _ := ConvertTextToBasic(tt.basicText)
|
||||||
|
if bytes.Compare(basic, tt.want) != 0 {
|
||||||
|
t.Errorf("%s\ngot '%#v'\nwant '%#v'\n", testname, basic, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue