consolidating modules

This commit is contained in:
Irmen de Jong
2022-03-11 20:35:25 +01:00
parent e51c274a18
commit 3961f26635
93 changed files with 181 additions and 344 deletions
+1 -2
View File
@@ -1,9 +1,8 @@
package prog8.code.core
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import prog8.code.core.*
import com.github.michaelbull.result.Result
class ZeropageAllocationError(message: String) : Exception(message)
@@ -0,0 +1,25 @@
package prog8.code.target
import prog8.code.core.*
import prog8.code.target.atari.AtariMachineDefinition
class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override val name = NAME
override val machine = AtariMachineDefinition()
override val supportedEncodings = setOf(Encoding.ATASCII)
override val defaultEncoding = Encoding.ATASCII
companion object {
const val NAME = "atari"
}
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> 6
else -> Int.MIN_VALUE
}
}
}
@@ -0,0 +1,20 @@
package prog8.code.target
import prog8.code.core.Encoding
import prog8.code.core.ICompilationTarget
import prog8.code.core.IMemSizer
import prog8.code.core.IStringEncoding
import prog8.code.target.c128.C128MachineDefinition
import prog8.code.target.cbm.CbmMemorySizer
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
override val name = NAME
override val machine = C128MachineDefinition()
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
override val defaultEncoding = Encoding.PETSCII
companion object {
const val NAME = "c128"
}
}
@@ -0,0 +1,20 @@
package prog8.code.target
import prog8.code.core.Encoding
import prog8.code.core.ICompilationTarget
import prog8.code.core.IMemSizer
import prog8.code.core.IStringEncoding
import prog8.code.target.c64.C64MachineDefinition
import prog8.code.target.cbm.CbmMemorySizer
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
override val name = NAME
override val machine = C64MachineDefinition()
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
override val defaultEncoding = Encoding.PETSCII
companion object {
const val NAME = "c64"
}
}
@@ -0,0 +1,20 @@
package prog8.code.target
import prog8.code.core.Encoding
import prog8.code.core.ICompilationTarget
import prog8.code.core.IMemSizer
import prog8.code.core.IStringEncoding
import prog8.code.target.cbm.CbmMemorySizer
import prog8.code.target.cx16.CX16MachineDefinition
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
override val name = NAME
override val machine = CX16MachineDefinition()
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES, Encoding.ISO)
override val defaultEncoding = Encoding.PETSCII
companion object {
const val NAME = "cx16"
}
}
+39
View File
@@ -0,0 +1,39 @@
package prog8.code.target
import com.github.michaelbull.result.fold
import prog8.code.core.Encoding
import prog8.code.core.IStringEncoding
import prog8.code.core.InternalCompilerException
import prog8.code.target.cbm.AtasciiEncoding
import prog8.code.target.cbm.IsoEncoding
import prog8.code.target.cbm.PetsciiEncoding
internal object Encoder: IStringEncoding {
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
val coded = when(encoding) {
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
Encoding.ISO -> IsoEncoding.encode(str)
Encoding.ATASCII -> AtasciiEncoding.encode(str)
else -> throw InternalCompilerException("unsupported encoding $encoding")
}
return coded.fold(
failure = { throw it },
success = { it }
)
}
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String {
val decoded = when(encoding) {
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
Encoding.ISO -> IsoEncoding.decode(bytes)
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
else -> throw InternalCompilerException("unsupported encoding $encoding")
}
return decoded.fold(
failure = { throw it },
success = { it }
)
}
}
@@ -0,0 +1,65 @@
package prog8.code.target.atari
import prog8.code.core.*
import prog8.code.target.c64.normal6502instructions
import java.nio.file.Path
class AtariMachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = 9.999999999e97
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
override val FLOAT_MEM_SIZE = 6
override val PROGRAM_LOAD_ADDRESS = 0x2000u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override val ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive // TODO
override val ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive // TODO
override lateinit var zeropage: Zeropage
override fun getFloat(num: Number) = TODO("float from number")
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib")
else
emptyList()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
val emulatorName: String
val cmdline: List<String>
when(selectedEmulator) {
1 -> {
emulatorName = "atari800"
cmdline = listOf(emulatorName, "-xl", "-xl-rev", "2", "-nobasic", "-run", "${programNameWithPath}.xex")
}
2 -> {
emulatorName = "altirra"
cmdline = listOf("Altirra64.exe", "${programNameWithPath.normalize()}.xex")
}
else -> {
System.err.println("Atari target only supports atari800 and altirra emulators.")
return
}
}
// TODO monlist?
println("\nStarting Atari800XL emulator $emulatorName...")
val processb = ProcessBuilder(cmdline).inheritIO()
val process: Process = processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu // TODO
override fun initializeZeropage(compilerOptions: CompilationOptions) {
zeropage = AtariZeropage(compilerOptions)
}
override val opcodeNames = normal6502instructions
}
@@ -0,0 +1,45 @@
package prog8.code.target.atari
import prog8.code.core.CompilationOptions
import prog8.code.core.InternalCompilerException
import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType
class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0xcbu // temp storage for a single byte
override val SCRATCH_REG = 0xccu // temp storage for a register, must be B1+1
override val SCRATCH_W1 = 0xcdu // temp storage 1 for a word $cd+$ce
override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use?
init {
if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE,
ZeropageType.DONTUSE
))
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
when (options.zeropage) {
ZeropageType.FULL -> {
// TODO all atari usable zero page locations, except the ones used by the system's IRQ routine
free.addAll(0x00u..0xffu)
// TODO atari free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
}
ZeropageType.KERNALSAFE -> {
free.addAll(0x80u..0xffu) // TODO
}
ZeropageType.BASICSAFE,
ZeropageType.FLOATSAFE -> {
free.addAll(0x80u..0xffu) // TODO
free.removeAll(0xd4u .. 0xefu) // floating point storage
}
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
}
removeReservedFromFreePool()
}
}
@@ -0,0 +1,55 @@
package prog8.code.target.c128
import prog8.code.core.*
import prog8.code.target.c64.normal6502instructions
import prog8.code.target.cbm.Mflpt5
import java.nio.file.Path
class C128MachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override val ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive
override val ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive
override lateinit var zeropage: Zeropage
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib")
else
emptyList()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
if(selectedEmulator!=1) {
System.err.println("The c128 target only supports the main emulator (Vice).")
return
}
println("\nStarting C-128 emulator x128...")
val viceMonlist = viceMonListName(programNameWithPath.toString())
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline).inheritIO()
val process: Process = processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
override fun initializeZeropage(compilerOptions: CompilationOptions) {
zeropage = C128Zeropage(compilerOptions)
}
override val opcodeNames = normal6502instructions
}
@@ -0,0 +1,43 @@
package prog8.code.target.c128
import prog8.code.core.CompilationOptions
import prog8.code.core.InternalCompilerException
import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType
class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x9bu // temp storage for a single byte
override val SCRATCH_REG = 0x9cu // temp storage for a register, must be B1+1
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
init {
if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE,
ZeropageType.DONTUSE
))
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
when (options.zeropage) {
ZeropageType.FULL -> {
// TODO all c128 usable zero page locations, except the ones used by the system's IRQ routine
free.addAll(0x0au..0xffu) // TODO c128 what about $02-$09?
// TODO c128 free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
}
ZeropageType.KERNALSAFE,
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE -> {
free.clear() // TODO c128 usable zero page addresses
}
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
}
removeReservedFromFreePool()
}
}
@@ -0,0 +1,76 @@
package prog8.code.target.c64
import prog8.code.core.*
import prog8.code.target.cbm.Mflpt5
import java.io.IOException
import java.nio.file.Path
class C64MachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x0801u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override val ESTACK_LO = 0xce00u // $ce00-$ceff inclusive
override val ESTACK_HI = 0xcf00u // $ce00-$ceff inclusive
override lateinit var zeropage: Zeropage
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib")
else
emptyList()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
if(selectedEmulator!=1) {
System.err.println("The c64 target only supports the main emulator (Vice).")
return
}
for(emulator in listOf("x64sc", "x64")) {
println("\nStarting C-64 emulator $emulator...")
val viceMonlist = viceMonListName(programNameWithPath.toString())
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline).inheritIO()
val process: Process
try {
process=processb.start()
} catch(x: IOException) {
continue // try the next emulator executable
}
process.waitFor()
break
}
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
override fun initializeZeropage(compilerOptions: CompilationOptions) {
zeropage = C64Zeropage(compilerOptions)
}
override val opcodeNames = normal6502instructions
}
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
internal val normal6502instructions = setOf(
"adc", "ahx", "alr", "anc", "and", "ane", "arr", "asl", "asr", "axs", "bcc", "bcs",
"beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc",
"cld", "cli", "clv", "cmp", "cpx", "cpy", "dcm", "dcp", "dec", "dex", "dey",
"eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs",
"inc", "ins", "inx", "iny", "isb", "isc", "jam", "jmp", "jsr", "lae", "las",
"lax", "lda", "lds", "ldx", "ldy", "lsr", "lxa", "nop", "ora", "pha", "php",
"pla", "plp", "rla", "rol", "ror", "rra", "rti", "rts", "sax", "sbc", "sbx",
"sec", "sed", "sei", "sha", "shl", "shr", "shs", "shx", "shy", "slo", "sre",
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")
@@ -0,0 +1,74 @@
package prog8.code.target.c64
import prog8.code.core.CompilationOptions
import prog8.code.core.InternalCompilerException
import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x02u // temp storage for a single byte
override val SCRATCH_REG = 0x03u // temp storage for a register, must be B1+1
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
init {
if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE,
ZeropageType.DONTUSE
))
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
if (options.zeropage == ZeropageType.FULL) {
free.addAll(0x02u..0xffu)
free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
} else {
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
free.addAll(listOf(
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
0x16, 0x17, 0x18, 0x19, 0x1a,
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
0x22, 0x23, 0x24, 0x25,
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
// 0x90-0xfa is 'kernal work storage area'
).map{it.toUInt()})
}
if (options.zeropage == ZeropageType.FLOATSAFE) {
// remove the zeropage locations used for floating point operations from the free list
free.removeAll(listOf(
0x22, 0x23, 0x24, 0x25,
0x10, 0x11, 0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
).map{it.toUInt()})
}
if(options.zeropage!= ZeropageType.DONTUSE) {
// add the free Zp addresses
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
free.addAll(listOf(0x04, 0x05, 0x06, 0x0a, 0x0e,
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6,
0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()})
} else {
// don't use the zeropage at all
free.clear()
}
}
removeReservedFromFreePool()
}
}
@@ -0,0 +1,204 @@
package prog8.code.target.cbm
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import java.io.CharConversionException
object AtasciiEncoding {
private val decodeTable: CharArray = charArrayOf(
// $00
'♥',
'├',
'\uf130', // 🮇 0x02 -> RIGHT ONE QUARTER BLOCK (CUS)
'┘',
'┤',
'┐',
'',
'╲',
'◢',
'▗',
'◣',
'▝',
'▘',
'\uf132', // 🮂 0x1d -> UPPER ONE QUARTER BLOCK (CUS)
'▂',
'▖',
// $10
'♣',
'┌',
'─',
'┼',
'•',
'▄',
'▎',
'┬',
'┴',
'▌',
'└',
'\u001b', // $1b = escape
'\ufffe', // UNDEFINED CHAR. $1c = cursor up
'\ufffe', // UNDEFINED CHAR. $1d = cursor down
'\ufffe', // UNDEFINED CHAR. $1e = cursor left
'\ufffe', // UNDEFINED CHAR. $1f = cursor right
// $20
' ',
'!',
'"',
'#',
'$',
'%',
'&',
'\'',
'(',
')',
'*',
'+',
',',
'-',
'.',
'/',
// $30
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
':',
';',
'<',
'=',
'>',
'?',
// $40
'@',
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
// $50
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z',
'[',
'\\',
']',
'^',
'_',
// $60
'♦',
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
// $70
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z',
'♠',
'|',
'\u000c', // $7d -> FORM FEED (CLEAR SCREEN)
'\u0008', // $7e -> BACKSPACE
'\u0009', // $7f -> TAB
// $80-$ff are reversed video characters + various special characters.
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
// $90
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
'\ufffe',
'\ufffe',
'\ufffe',
'\n', // $9b -> EOL/RETURN
'\ufffe', // UNDEFINED $9c = DELETE LINE
'\ufffe', // UNDEFINED $9d = INSERT LINE
'\ufffe', // UNDEFINED $9e = CLEAR TAB STOP
'\ufffe', // UNDEFINED $9f = SET TAB STOP
// $a0
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
// $b0
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
// $c0
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
// $d0
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
// $e0
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
// $f0
'\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe', '\ufffe',
'\ufffe',
'\ufffe',
'\ufffe',
'\ufffe',
'\ufffe',
'\u0007', // $fd = bell/beep
'\u007f', // $fe = DELETE
'\ufffe' // UNDEFINED $ff = INSERT
)
private val encodeTable = decodeTable.withIndex().associate{it.value to it.index}
fun encode(str: String): Result<List<UByte>, CharConversionException> {
return Ok(str.map { encodeTable.getValue(it).toUByte() })
}
fun decode(bytes: List<UByte>): Result<String, CharConversionException> {
return Ok(bytes.map { decodeTable[it.toInt()] }.joinToString(""))
}
}
@@ -0,0 +1,15 @@
package prog8.code.target.cbm
import prog8.code.core.*
internal object CbmMemorySizer: IMemSizer {
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE
else -> Int.MIN_VALUE
}
}
}
@@ -0,0 +1,27 @@
package prog8.code.target.cbm
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import java.io.CharConversionException
import java.nio.charset.Charset
object IsoEncoding {
val charset: Charset = Charset.forName("ISO-8859-15")
fun encode(str: String): Result<List<UByte>, CharConversionException> {
return try {
Ok(str.toByteArray(charset).map { it.toUByte() })
} catch (ce: CharConversionException) {
Err(ce)
}
}
fun decode(bytes: List<UByte>): Result<String, CharConversionException> {
return try {
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
} catch (ce: CharConversionException) {
Err(ce)
}
}
}
@@ -0,0 +1,78 @@
package prog8.code.target.cbm
import prog8.code.core.IMachineFloat
import prog8.code.core.InternalCompilerException
import kotlin.math.absoluteValue
import kotlin.math.pow
data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte): IMachineFloat {
companion object {
const val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
const val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
const val FLOAT_MEM_SIZE = 5
val zero = Mflpt5(0u, 0u, 0u, 0u, 0u)
fun fromNumber(num: Number): Mflpt5 {
// see https://en.wikipedia.org/wiki/Microsoft_Binary_Format
// and https://sourceforge.net/p/acme-crossass/code-0/62/tree/trunk/ACME_Lib/cbm/mflpt.a
// and https://en.wikipedia.org/wiki/IEEE_754-1985
val flt = num.toDouble()
if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this")
if (flt == 0.0)
return zero
val sign = if (flt < 0.0) 0x80L else 0x00L
var exponent = 128 + 32 // 128 is cbm's bias, 32 is this algo's bias
var mantissa = flt.absoluteValue
// if mantissa is too large, shift right and adjust exponent
while (mantissa >= 0x100000000) {
mantissa /= 2.0
exponent++
}
// if mantissa is too small, shift left and adjust exponent
while (mantissa < 0x80000000) {
mantissa *= 2.0
exponent--
}
return when {
exponent < 0 -> zero // underflow, use zero instead
exponent > 255 -> throw InternalCompilerException("floating point overflow: $this")
exponent == 0 -> zero
else -> {
val mantLong = mantissa.toLong()
Mflpt5(
exponent.toUByte(),
(mantLong.and(0x7f000000L) ushr 24).or(sign).toUByte(),
(mantLong.and(0x00ff0000L) ushr 16).toUByte(),
(mantLong.and(0x0000ff00L) ushr 8).toUByte(),
(mantLong.and(0x000000ffL)).toUByte()
)
}
}
}
}
override fun toDouble(): Double {
if (this == zero) return 0.0
val exp = b0.toInt() - 128
val sign = (b1.toInt() and 0x80) > 0
val number = 0x80000000L.or(b1.toLong() shl 24).or(b2.toLong() shl 16).or(b3.toLong() shl 8).or(b4.toLong())
val result = number.toDouble() * (2.0).pow(exp) / 0x100000000
return if (sign) -result else result
}
override fun makeFloatFillAsm(): String {
val b0 = "$" + b0.toString(16).padStart(2, '0')
val b1 = "$" + b1.toString(16).padStart(2, '0')
val b2 = "$" + b2.toString(16).padStart(2, '0')
val b3 = "$" + b3.toString(16).padStart(2, '0')
val b4 = "$" + b4.toString(16).padStart(2, '0')
return "$b0, $b1, $b2, $b3, $b4"
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,77 @@
package prog8.code.target.cx16
import prog8.code.core.*
import prog8.code.target.cbm.Mflpt5
import java.nio.file.Path
class CX16MachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU65c02
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x0801u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override val ESTACK_LO = 0x0400u // $0400-$04ff inclusive
override val ESTACK_HI = 0x0500u // $0500-$05ff inclusive
override lateinit var zeropage: Zeropage
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib")
else
emptyList()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
val emulator: String
val extraArgs: List<String>
when(selectedEmulator) {
1 -> {
emulator = "x16emu"
extraArgs = emptyList()
}
2 -> {
emulator = "box16"
extraArgs = listOf("-sym", viceMonListName(programNameWithPath.toString()))
}
else -> {
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
return
}
}
println("\nStarting Commander X16 emulator $emulator...")
val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
val processb = ProcessBuilder(cmdline).inheritIO()
val process: Process = processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
override fun initializeZeropage(compilerOptions: CompilationOptions) {
zeropage = CX16Zeropage(compilerOptions)
}
// 65c02 opcodes, these cannot be used as variable or label names
override val opcodeNames = setOf("adc", "and", "asl", "bcc", "bcs",
"beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc",
"cld", "cli", "clv", "cmp", "cpx", "cpy", "dec", "dex", "dey",
"eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs",
"inc", "inx", "iny", "jmp", "jsr",
"lda", "ldx", "ldy", "lsr", "nop", "ora", "pha", "php",
"pla", "plp", "rol", "ror", "rti", "rts", "sbc",
"sec", "sed", "sei",
"sta", "stx", "sty", "tax", "tay", "tsx", "txa", "txs", "tya",
"bra", "phx", "phy", "plx", "ply", "stz", "trb", "tsb", "bbr", "bbs",
"rmb", "smb", "stp", "wai")
}
@@ -0,0 +1,52 @@
package prog8.code.target.cx16
import prog8.code.core.*
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x7au // temp storage for a single byte
override val SCRATCH_REG = 0x7bu // temp storage for a register, must be B1+1
override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
init {
if (options.floats && options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE))
throw InternalCompilerException("when floats are enabled, zero page type should be 'basicsafe' or 'dontuse'")
// the addresses 0x02 to 0x21 (inclusive) are taken for sixteen virtual 16-bit api registers.
synchronized(this) {
when (options.zeropage) {
ZeropageType.FULL -> {
free.addAll(0x22u..0xffu)
}
ZeropageType.KERNALSAFE -> {
free.addAll(0x22u..0x7fu)
free.addAll(0xa9u..0xffu)
}
ZeropageType.BASICSAFE -> {
free.addAll(0x22u..0x7fu)
}
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
}
removeReservedFromFreePool()
// note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
// however, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
for(reg in 0..15) {
allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
allocatedVariables[listOf("cx16", "r${reg}L")] = ZpAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
allocatedVariables[listOf("cx16", "r${reg}H")] = ZpAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
allocatedVariables[listOf("cx16", "r${reg}sL")] = ZpAllocation((2+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables[listOf("cx16", "r${reg}sH")] = ZpAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
}
}
}
}