mirror of
https://github.com/tjboldt/ProDOS-Utilities.git
synced 2024-12-27 05:29:49 +00:00
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
|
||||
1. Allow > 128 KB file support
|
||||
2. Create/Delete directories
|
||||
3. Add file/directory/basic tests
|
||||
3. Add file/directory tests
|
||||
4. Add rename
|
||||
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.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(&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.Parse()
|
||||
|
||||
@ -117,9 +117,15 @@ func main() {
|
||||
fmt.Printf("Failed to open input file %s: %s", inFileName, err)
|
||||
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
|
||||
if // Magic number
|
||||
binary.BigEndian.Uint32(inFile[0x00:]) == 0x00051600 &&
|
||||
if // Magic number
|
||||
binary.BigEndian.Uint32(inFile[0x00:]) == 0x00051600 &&
|
||||
// Version number
|
||||
binary.BigEndian.Uint32(inFile[0x04:]) == 0x00020000 &&
|
||||
// Number of entries
|
||||
@ -129,7 +135,7 @@ func main() {
|
||||
// Offset
|
||||
binary.BigEndian.Uint32(inFile[0x1E:]) == 0x0000003A &&
|
||||
// Length
|
||||
binary.BigEndian.Uint32(inFile[0x22:]) == uint32(len(inFile)) - 0x3A &&
|
||||
binary.BigEndian.Uint32(inFile[0x22:]) == uint32(len(inFile))-0x3A &&
|
||||
// ProDOS File Info ID
|
||||
binary.BigEndian.Uint32(inFile[0x26:]) == 0x0000000B &&
|
||||
// Offset
|
||||
|
193
prodos/basic.go
193
prodos/basic.go
@ -7,7 +7,10 @@
|
||||
package prodos
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -88,7 +91,7 @@ var tokens = map[byte]string{
|
||||
0xC9: "-",
|
||||
0xCA: "*",
|
||||
0xCB: "/",
|
||||
0xCC: ";",
|
||||
//0xCC: ";", // fails if this is there
|
||||
0xCD: "AND",
|
||||
0xCE: "OR",
|
||||
0xCF: ">",
|
||||
@ -161,95 +164,123 @@ func ConvertBasicToText(basic []byte) string {
|
||||
}
|
||||
}
|
||||
|
||||
// func ConvertTextToBasic(text string) ([]byte, error) {
|
||||
// // convert line endings
|
||||
// text = strings.Replace(text, "\r\n", "\n", -1)
|
||||
// text = strings.Replace(text, "\r", "\n", -1)
|
||||
func ConvertTextToBasic(text string) ([]byte, error) {
|
||||
// convert line endings
|
||||
text = strings.Replace(text, "\r\n", "\n", -1)
|
||||
text = strings.Replace(text, "\r", "\n", -1)
|
||||
|
||||
// const startState = 0
|
||||
// const lineNumberState = 1
|
||||
// const tokenCheckState = 2
|
||||
// const literalState = 3
|
||||
// const stringState = 4
|
||||
// const dataState = 5
|
||||
// const endOfLineState = 6
|
||||
starting := true
|
||||
parsingLineNumber := false
|
||||
parsingData := false
|
||||
parsingString := false
|
||||
parsingRem := false
|
||||
foundToken := false
|
||||
|
||||
// state := startState
|
||||
currentByte := 0x0801
|
||||
var lineNumberString string
|
||||
|
||||
// currentByte := 0x0801
|
||||
// var lineNumberString string
|
||||
// var tokenString string
|
||||
basicFile := new(bytes.Buffer)
|
||||
basicLine := new(bytes.Buffer)
|
||||
|
||||
// basicFile := new(bytes.Buffer)
|
||||
// basicLine := new(bytes.Buffer)
|
||||
skipChars := 0
|
||||
|
||||
// // parse character by character
|
||||
// for _, c := range text {
|
||||
// parse character by character
|
||||
for index, c := range text {
|
||||
|
||||
// // skip initial whitespace and look for the start of a line number
|
||||
// if state == startState {
|
||||
// if c == ' ' {
|
||||
// continue
|
||||
// }
|
||||
// if c >= '0' && c <= '9' {
|
||||
// state = lineNumberState
|
||||
// } else {
|
||||
// return nil, errors.New("unexpected character before line number")
|
||||
// }
|
||||
// }
|
||||
// skip initial whitespace and look for the start of a line number
|
||||
if starting {
|
||||
if c == ' ' {
|
||||
continue
|
||||
}
|
||||
if c >= '0' && c <= '9' {
|
||||
starting = false
|
||||
parsingLineNumber = true
|
||||
} else {
|
||||
return nil, errors.New("unexpected character before line number")
|
||||
}
|
||||
}
|
||||
|
||||
// // parse line number
|
||||
// if state == lineNumberState {
|
||||
// if c >= '0' && c <= '9' {
|
||||
// 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 skipChars > 0 && c != '\n' {
|
||||
skipChars--
|
||||
continue
|
||||
}
|
||||
|
||||
// if state == tokenCheckState {
|
||||
// // skip initial whitespace
|
||||
// if c == ' ' && len(tokenString) == 0 {
|
||||
// continue
|
||||
// }
|
||||
// parse line number
|
||||
if parsingLineNumber {
|
||||
if c >= '0' && c <= '9' {
|
||||
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 c == '\n' {
|
||||
// state = endOfLineState
|
||||
// } else if c == '"' {
|
||||
// state = stringState
|
||||
// }
|
||||
if !parsingLineNumber {
|
||||
if c == '\n' {
|
||||
starting = true
|
||||
parsingLineNumber = false
|
||||
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 {
|
||||
// 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
|
||||
// }
|
||||
return basicFile.Bytes(), nil
|
||||
}
|
||||
|
76
prodos/basic_test.go
Normal file
76
prodos/basic_test.go
Normal file
@ -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
Block a user