added 'atari' compiler target beginnings (Atari 800 XL)

also default char and string encoding now taken from compiler target
This commit is contained in:
Irmen de Jong 2022-02-21 23:38:53 +01:00
parent 553f3b45d2
commit 6a0551cea1
29 changed files with 1022 additions and 93 deletions

View File

@ -42,6 +42,10 @@ class AsmGen(internal val program: Program,
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen, allocator)
override fun compileToAssembly(): IAssemblyProgram? {
if(options.compTarget.name=="atari" && options.launcher==LauncherType.BASIC)
throw AssemblyError("atari target cannot use CBM BASIC launcher type")
assemblyLines.clear()
loopEndLabels.clear()

View File

@ -17,39 +17,82 @@ internal class AssemblyProgram(
private val compTarget: ICompilationTarget) : IAssemblyProgram {
private val assemblyFile = outputDir.resolve("$name.asm")
private val prgFile = outputDir.resolve("$name.prg")
private val prgFile = outputDir.resolve("$name.prg") // CBM prg executable program
private val xexFile = outputDir.resolve("$name.xex") // Atari xex executable program
private val binFile = outputDir.resolve("$name.bin")
private val viceMonListFile = outputDir.resolve(viceMonListName(name))
private val listFile = outputDir.resolve("$name.list")
override fun assemble(options: CompilationOptions): Boolean {
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile", "--no-monitor"
)
if(options.asmQuiet)
command.add("--quiet")
val assemblerCommand: List<String>
if(options.asmListfile)
command.add("--list=$listFile")
when (compTarget.name) {
in setOf("c64", "c128", "cx16") -> {
// CBM machines .prg generation.
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile", "--no-monitor"
)
if(options.asmQuiet)
command.add("--quiet")
if(options.asmListfile)
command.add("--list=$listFile")
val outFile = when (options.output) {
OutputType.PRG -> {
command.add("--cbm-prg")
println("\nCreating prg for target ${compTarget.name}.")
prgFile
}
OutputType.RAW -> {
command.add("--nostart")
println("\nCreating raw binary for target ${compTarget.name}.")
binFile
}
}
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
assemblerCommand = command
val outFile = when (options.output) {
OutputType.PRG -> {
command.add("--cbm-prg")
println("\nCreating prg for target ${compTarget.name}.")
prgFile
}
OutputType.RAW -> {
command.add("--nostart")
println("\nCreating raw binary for target ${compTarget.name}.")
binFile
"atari" -> {
// Atari800XL .xex generation.
// TODO are these options okay?
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
"--no-monitor"
)
if(options.asmQuiet)
command.add("--quiet")
if(options.asmListfile)
command.add("--list=$listFile")
val outFile = when (options.output) {
OutputType.PRG -> {
command.add("--atari-xex")
println("\nCreating xex for target ${compTarget.name}.")
xexFile
}
OutputType.RAW -> {
command.add("--nostart")
println("\nCreating raw binary for target ${compTarget.name}.")
binFile
}
}
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
assemblerCommand = command
}
else -> throw AssemblyError("invalid compilation target")
}
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
val proc = ProcessBuilder(command).inheritIO().start()
val proc = ProcessBuilder(assemblerCommand).inheritIO().start()
val result = proc.waitFor()
if (result == 0) {
removeGeneratedLabelsFromMonlist()

View File

@ -0,0 +1,53 @@
package prog8.codegen.target
import prog8.ast.base.*
import prog8.ast.expressions.Expression
import prog8.ast.expressions.StringLiteral
import prog8.ast.statements.RegisterOrStatusflag
import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl
import prog8.codegen.target.atari.AtariMachineDefinition
import prog8.codegen.target.cbm.asmsub6502ArgsEvalOrder
import prog8.codegen.target.cbm.asmsub6502ArgsHaveRegisterClobberRisk
import prog8.compilerinterface.*
class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override val name = NAME
override val machine = AtariMachineDefinition()
override val supportedEncodings = setOf(Encoding.ISO) // TODO
override val defaultEncoding = Encoding.ISO // TODO
override val defaultLauncherType = LauncherType.NONE
companion object {
const val NAME = "atari"
}
override fun asmsubArgsEvalOrder(sub: Subroutine): List<Int> =
asmsub6502ArgsEvalOrder(sub)
override fun asmsubArgsHaveRegisterClobberRisk(args: List<Expression>, paramRegisters: List<RegisterOrStatusflag>) =
asmsub6502ArgsHaveRegisterClobberRisk(args, paramRegisters)
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> 6
else -> Int.MIN_VALUE
}
}
override fun memorySize(decl: VarDecl): Int {
return when(decl.type) {
VarDeclType.CONST -> 0
VarDeclType.VAR, VarDeclType.MEMORY -> {
when(val dt = decl.datatype) {
in NumericDatatypes -> return memorySize(dt)
in ArrayDatatypes -> decl.arraysize!!.constIndex()!! * memorySize(ArrayToElementTypes.getValue(dt))
DataType.STR -> (decl.value as StringLiteral).value.length + 1
else -> 0
}
}
}
}
}

View File

@ -7,16 +7,15 @@ import prog8.codegen.target.c128.C128MachineDefinition
import prog8.codegen.target.cbm.CbmMemorySizer
import prog8.codegen.target.cbm.asmsub6502ArgsEvalOrder
import prog8.codegen.target.cbm.asmsub6502ArgsHaveRegisterClobberRisk
import prog8.compilerinterface.Encoding
import prog8.compilerinterface.ICompilationTarget
import prog8.compilerinterface.IMemSizer
import prog8.compilerinterface.IStringEncoding
import prog8.compilerinterface.*
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
override val defaultLauncherType = LauncherType.BASIC
companion object {
const val NAME = "c128"

View File

@ -7,16 +7,15 @@ import prog8.codegen.target.c64.C64MachineDefinition
import prog8.codegen.target.cbm.CbmMemorySizer
import prog8.codegen.target.cbm.asmsub6502ArgsEvalOrder
import prog8.codegen.target.cbm.asmsub6502ArgsHaveRegisterClobberRisk
import prog8.compilerinterface.Encoding
import prog8.compilerinterface.ICompilationTarget
import prog8.compilerinterface.IMemSizer
import prog8.compilerinterface.IStringEncoding
import prog8.compilerinterface.*
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
override val defaultLauncherType = LauncherType.BASIC
companion object {
const val NAME = "c64"

View File

@ -7,16 +7,15 @@ import prog8.codegen.target.cbm.CbmMemorySizer
import prog8.codegen.target.cbm.asmsub6502ArgsEvalOrder
import prog8.codegen.target.cbm.asmsub6502ArgsHaveRegisterClobberRisk
import prog8.codegen.target.cx16.CX16MachineDefinition
import prog8.compilerinterface.Encoding
import prog8.compilerinterface.ICompilationTarget
import prog8.compilerinterface.IMemSizer
import prog8.compilerinterface.IStringEncoding
import prog8.compilerinterface.*
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
override val defaultLauncherType = LauncherType.BASIC
companion object {
const val NAME = "cx16"

View File

@ -0,0 +1,62 @@
package prog8.codegen.target.atari
import prog8.codegen.target.c64.normal6502instructions
import prog8.compilerinterface.*
import java.io.IOException
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 BASIC_LOAD_ADDRESS = 0x2000u
override val RAW_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 == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib")
else
emptyList()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
if(selectedEmulator!=1) {
System.err.println("The atari target only supports the main emulator (atari800).")
return
}
for(emulator in listOf("atari800")) {
println("\nStarting Atari800XL emulator $emulator...")
val cmdline = listOf(emulator, "-xl", "-nobasic", "-run", "${programNameWithPath}.xex")
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 // TODO
override fun initializeZeropage(compilerOptions: CompilationOptions) {
zeropage = AtariZeropage(compilerOptions)
}
override val opcodeNames = normal6502instructions
}

View File

@ -0,0 +1,45 @@
package prog8.codegen.target.atari
import prog8.compilerinterface.CompilationOptions
import prog8.compilerinterface.InternalCompilerException
import prog8.compilerinterface.Zeropage
import prog8.compilerinterface.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()
}
}

View File

@ -0,0 +1,307 @@
; Prog8 definitions for the Atari800XL
; Including memory registers, I/O registers, Basic and Kernal subroutines.
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
atari {
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
; ---- kernal routines ----
; TODO
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
; TODO
%asm {{
sei
cld
clc
; TODO reset screen mode etc etc
clv
cli
rts
}}
}
asmsub init_system_phase2() {
%asm {{
rts ; no phase 2 steps on the Atari
}}
}
}
sys {
; ------- lowlevel system routines --------
const ubyte target = 8 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 8 = atari800XL
asmsub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt.
; TODO
%asm {{
sei
jmp (atari.RESET_VEC)
}}
}
sub wait(uword jiffies) {
; --- wait approximately the given number of jiffies (1/60th seconds)
; TODO
}
asmsub waitvsync() clobbers(A) {
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
; TODO
%asm {{
nop
rts
}}
}
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
; note: can't be inlined because is called from asm as well
%asm {{
ldx cx16.r0
stx P8ZP_SCRATCH_W1 ; source in ZP
ldx cx16.r0+1
stx P8ZP_SCRATCH_W1+1
ldx cx16.r1
stx P8ZP_SCRATCH_W2 ; target in ZP
ldx cx16.r1+1
stx P8ZP_SCRATCH_W2+1
cpy #0
bne _longcopy
; copy <= 255 bytes
tay
bne _copyshort
rts ; nothing to copy
_copyshort
; decrease source and target pointers so we can simply index by Y
lda P8ZP_SCRATCH_W1
bne +
dec P8ZP_SCRATCH_W1+1
+ dec P8ZP_SCRATCH_W1
lda P8ZP_SCRATCH_W2
bne +
dec P8ZP_SCRATCH_W2+1
+ dec P8ZP_SCRATCH_W2
- lda (P8ZP_SCRATCH_W1),y
sta (P8ZP_SCRATCH_W2),y
dey
bne -
rts
_longcopy
sta P8ZP_SCRATCH_B1 ; lsb(count) = remainder in last page
tya
tax ; x = num pages (1+)
ldy #0
- lda (P8ZP_SCRATCH_W1),y
sta (P8ZP_SCRATCH_W2),y
iny
bne -
inc P8ZP_SCRATCH_W1+1
inc P8ZP_SCRATCH_W2+1
dex
bne -
ldy P8ZP_SCRATCH_B1
bne _copyshort
rts
}}
}
asmsub memset(uword mem @R0, uword numbytes @R1, ubyte value @A) clobbers(A,X,Y) {
%asm {{
ldy cx16.r0
sty P8ZP_SCRATCH_W1
ldy cx16.r0+1
sty P8ZP_SCRATCH_W1+1
ldx cx16.r1
ldy cx16.r1+1
jmp prog8_lib.memset
}}
}
asmsub memsetw(uword mem @R0, uword numwords @R1, uword value @AY) clobbers(A,X,Y) {
%asm {{
ldx cx16.r0
stx P8ZP_SCRATCH_W1
ldx cx16.r0+1
stx P8ZP_SCRATCH_W1+1
ldx cx16.r1
stx P8ZP_SCRATCH_W2
ldx cx16.r1+1
stx P8ZP_SCRATCH_W2+1
jmp prog8_lib.memsetw
}}
}
inline asmsub read_flags() -> ubyte @A {
%asm {{
php
pla
}}
}
inline asmsub clear_carry() {
%asm {{
clc
}}
}
inline asmsub set_carry() {
%asm {{
sec
}}
}
inline asmsub clear_irqd() {
%asm {{
cli
}}
}
inline asmsub set_irqd() {
%asm {{
sei
}}
}
inline asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register
; TODO
%asm {{
ldx prog8_lib.orig_stackpointer
txs
rts ; return to original caller
}}
}
inline asmsub progend() -> uword @AY {
%asm {{
lda #<prog8_program_end
ldy #>prog8_program_end
}}
}
}
cx16 {
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
; they are simulated on the Atari as well but their location in memory is different
; TODO
&uword r0 = $1b00
&uword r1 = $1b02
&uword r2 = $1b04
&uword r3 = $1b06
&uword r4 = $1b08
&uword r5 = $1b0a
&uword r6 = $1b0c
&uword r7 = $1b0e
&uword r8 = $1b10
&uword r9 = $1b12
&uword r10 = $1b14
&uword r11 = $1b16
&uword r12 = $1b18
&uword r13 = $1b1a
&uword r14 = $1b1c
&uword r15 = $1b1e
&word r0s = $1b00
&word r1s = $1b02
&word r2s = $1b04
&word r3s = $1b06
&word r4s = $1b08
&word r5s = $1b0a
&word r6s = $1b0c
&word r7s = $1b0e
&word r8s = $1b10
&word r9s = $1b12
&word r10s = $1b14
&word r11s = $1b16
&word r12s = $1b18
&word r13s = $1b1a
&word r14s = $1b1c
&word r15s = $1b1e
&ubyte r0L = $1b00
&ubyte r1L = $1b02
&ubyte r2L = $1b04
&ubyte r3L = $1b06
&ubyte r4L = $1b08
&ubyte r5L = $1b0a
&ubyte r6L = $1b0c
&ubyte r7L = $1b0e
&ubyte r8L = $1b10
&ubyte r9L = $1b12
&ubyte r10L = $1b14
&ubyte r11L = $1b16
&ubyte r12L = $1b18
&ubyte r13L = $1b1a
&ubyte r14L = $1b1c
&ubyte r15L = $1b1e
&ubyte r0H = $1b01
&ubyte r1H = $1b03
&ubyte r2H = $1b05
&ubyte r3H = $1b07
&ubyte r4H = $1b09
&ubyte r5H = $1b0b
&ubyte r6H = $1b0d
&ubyte r7H = $1b0f
&ubyte r8H = $1b11
&ubyte r9H = $1b13
&ubyte r10H = $1b15
&ubyte r11H = $1b17
&ubyte r12H = $1b19
&ubyte r13H = $1b1b
&ubyte r14H = $1b1d
&ubyte r15H = $1b1f
&byte r0sL = $1b00
&byte r1sL = $1b02
&byte r2sL = $1b04
&byte r3sL = $1b06
&byte r4sL = $1b08
&byte r5sL = $1b0a
&byte r6sL = $1b0c
&byte r7sL = $1b0e
&byte r8sL = $1b10
&byte r9sL = $1b12
&byte r10sL = $1b14
&byte r11sL = $1b16
&byte r12sL = $1b18
&byte r13sL = $1b1a
&byte r14sL = $1b1c
&byte r15sL = $1b1e
&byte r0sH = $1b01
&byte r1sH = $1b03
&byte r2sH = $1b05
&byte r3sH = $1b07
&byte r4sH = $1b09
&byte r5sH = $1b0b
&byte r6sH = $1b0d
&byte r7sH = $1b0f
&byte r8sH = $1b11
&byte r9sH = $1b13
&byte r10sH = $1b15
&byte r11sH = $1b17
&byte r12sH = $1b19
&byte r13sH = $1b1b
&byte r14sH = $1b1d
&byte r15sH = $1b1f
}

View File

@ -0,0 +1,402 @@
; Prog8 definitions for the Text I/O and Screen routines for the Atari 800XL
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
%import syslib
%import conv
txt {
const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 25
sub clear_screen() {
txt.chrout(147) ; TODO
}
sub home() {
txt.chrout(19) ; TODO
}
sub nl() {
txt.chrout('\n')
}
sub spc() {
txt.chrout(' ')
}
asmsub column(ubyte col @A) clobbers(A, X, Y) {
; ---- set the cursor on the given column (starting with 0) on the current line
; TODO
%asm {{
rts
}}
}
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
; ---- fill the character screen with the given fill character and character color.
; (assumes screen and color matrix are at their default addresses)
; TODO
%asm {{
rts
}}
}
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
; ---- clear the character screen with the given fill character (leaves colors)
; (assumes screen matrix is at the default address)
; TODO
%asm {{
rts
}}
}
asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
; ---- clear the character screen colors with the given color (leaves characters).
; (assumes color matrix is at the default address)
; TODO
%asm {{
rts
}}
}
sub color (ubyte txtcol) {
; TODO
}
sub lowercase() {
; TODO
}
sub uppercase() {
; TODO
}
asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
; ---- scroll the whole screen 1 character to the left
; contents of the rightmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too
; TODO
%asm {{
rts
}}
}
asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too
; TODO
%asm {{
rts
}}
}
asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character up
; contents of the bottom row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too
; TODO
%asm {{
rts
}}
}
asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character down
; contents of the top row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too
; TODO
%asm {{
rts
}}
}
romsub $FFD2 = chrout(ubyte char @ A) ; TODO
asmsub print (str text @ AY) clobbers(A,Y) {
; ---- print null terminated string from A/Y
; note: the compiler contains an optimization that will replace
; a call to this subroutine with a string argument of just one char,
; by just one call to CHROUT of that single char.
%asm {{
sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG
ldy #0
- lda (P8ZP_SCRATCH_B1),y
beq +
jsr chrout
iny
bne -
+ rts
}}
}
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.ubyte2decimal
pha
tya
jsr chrout
pla
jsr chrout
txa
jsr chrout
ldx P8ZP_SCRATCH_REG
rts
}}
}
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
; ---- print the ubyte in A in decimal form, without left padding 0s
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.ubyte2decimal
_print_byte_digits
pha
cpy #'0'
beq +
tya
jsr chrout
pla
jsr chrout
jmp _ones
+ pla
cmp #'0'
beq _ones
jsr chrout
_ones txa
jsr chrout
ldx P8ZP_SCRATCH_REG
rts
}}
}
asmsub print_b (byte value @ A) clobbers(A,Y) {
; ---- print the byte in A in decimal form, without left padding 0s
%asm {{
stx P8ZP_SCRATCH_REG
pha
cmp #0
bpl +
lda #'-'
jsr chrout
+ pla
jsr conv.byte2decimal
jmp print_ub._print_byte_digits
}}
}
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
stx P8ZP_SCRATCH_REG
bcc +
pha
lda #'$'
jsr chrout
pla
+ jsr conv.ubyte2hex
jsr chrout
tya
jsr chrout
ldx P8ZP_SCRATCH_REG
rts
}}
}
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
stx P8ZP_SCRATCH_REG
sta P8ZP_SCRATCH_B1
bcc +
lda #'%'
jsr chrout
+ ldy #8
- lda #'0'
asl P8ZP_SCRATCH_B1
bcc +
lda #'1'
+ jsr chrout
dey
bne -
ldx P8ZP_SCRATCH_REG
rts
}}
}
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
pha
tya
jsr print_ubbin
pla
clc
jmp print_ubbin
}}
}
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
pha
tya
jsr print_ubhex
pla
clc
jmp print_ubhex
}}
}
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.uword2decimal
ldy #0
- lda conv.uword2decimal.decTenThousands,y
beq +
jsr chrout
iny
bne -
+ ldx P8ZP_SCRATCH_REG
rts
}}
}
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
; ---- print the uword in A/Y in decimal form, without left padding 0s
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.uword2decimal
ldx P8ZP_SCRATCH_REG
ldy #0
- lda conv.uword2decimal.decTenThousands,y
beq _allzero
cmp #'0'
bne _gotdigit
iny
bne -
_gotdigit
jsr chrout
iny
lda conv.uword2decimal.decTenThousands,y
bne _gotdigit
rts
_allzero
lda #'0'
jmp chrout
}}
}
asmsub print_w (word value @ AY) clobbers(A,Y) {
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
%asm {{
cpy #0
bpl +
pha
lda #'-'
jsr chrout
tya
eor #255
tay
pla
eor #255
clc
adc #1
bcc +
iny
+ jmp print_uw
}}
}
asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
; ---- Input a string (max. 80 chars) from the keyboard. Returns length in Y. (string is terminated with a 0 byte as well)
; It assumes the keyboard is selected as I/O channel!
; TODO
%asm {{
ldy #0
rts
}}
}
asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y) {
; ---- sets the character in the screen matrix at the given position
; TODO
%asm {{
rts
}}
}
asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
; ---- get the character in the screen matrix at the given location
; TODO
%asm {{
rts
}}
}
asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A, Y) {
; ---- set the color in A on the screen matrix at the given position
; TODO
%asm {{
rts
}}
}
asmsub getclr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
; ---- get the color in the screen color matrix at the given location
; TODO
%asm {{
rts
}}
}
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
; ---- set char+color at the given position on the screen
; TODO
%asm {{
rts
}}
}
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
; ---- set cursor at specific position
; TODO
%asm {{
rts
}}
}
asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns)
; TODO
%asm {{
lda #0
rts
}}
}
asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows)
; TODO
%asm {{
lda #0
rts
}}
}
}

View File

@ -2,6 +2,7 @@ package prog8
import kotlinx.cli.*
import prog8.ast.base.AstException
import prog8.codegen.target.AtariTarget
import prog8.codegen.target.C128Target
import prog8.codegen.target.C64Target
import prog8.codegen.target.Cx16Target
@ -44,7 +45,7 @@ private fun compileMain(args: Array<String>): Boolean {
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
val asmListfile by cli.option(ArgType.Boolean, fullName = "asmlist", description = "make the assembler produce a listing file as well")
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental codegen")
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}')").default(C64Target.NAME)
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}')").default(C64Target.NAME)
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
@ -71,7 +72,7 @@ private fun compileMain(args: Array<String>): Boolean {
if(srcdirs.firstOrNull()!=".")
srcdirs.add(0, ".")
if (compilationTarget !in setOf(C64Target.NAME, C128Target.NAME, Cx16Target.NAME)) {
if (compilationTarget !in setOf(C64Target.NAME, C128Target.NAME, Cx16Target.NAME, AtariTarget.NAME)) {
System.err.println("Invalid compilation target: $compilationTarget")
return false
}

View File

@ -12,6 +12,7 @@ import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Directive
import prog8.ast.statements.VarDecl
import prog8.ast.walk.IAstVisitor
import prog8.codegen.target.AtariTarget
import prog8.codegen.target.C128Target
import prog8.codegen.target.C64Target
import prog8.codegen.target.Cx16Target
@ -59,6 +60,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult {
C64Target.NAME -> C64Target()
C128Target.NAME -> C128Target()
Cx16Target.NAME -> Cx16Target()
AtariTarget.NAME -> AtariTarget()
else -> throw IllegalArgumentException("invalid compilation target")
}
@ -206,6 +208,9 @@ fun parseImports(filepath: Path,
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
errors.err("BASIC launcher requires output type PRG", program.toplevelModule.position)
if(compilerOptions.launcher == LauncherType.BASIC && compTarget.name==AtariTarget.NAME)
errors.err("atari target cannot use CBM BASIC launcher, use NONE", program.toplevelModule.position)
errors.report()
return Triple(program, compilerOptions, importedFiles)
@ -255,7 +260,7 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
OutputType.PRG
}
}
val launcherType = if (launcherTypeStr == null) LauncherType.BASIC else {
val launcherType = if (launcherTypeStr == null) compTarget.defaultLauncherType else {
try {
LauncherType.valueOf(launcherTypeStr)
} catch (x: IllegalArgumentException) {
@ -274,7 +279,7 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
println("Analyzing code...")
program.preprocessAst(errors)
program.preprocessAst(errors, compilerOptions.compTarget)
program.checkIdentifiers(errors, compilerOptions)
errors.report()
program.charLiteralsToUByteLiterals(compilerOptions.compTarget, errors)

View File

@ -10,10 +10,7 @@ import prog8.ast.statements.Directive
import prog8.ast.statements.VarDeclOrigin
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compilerinterface.CompilationOptions
import prog8.compilerinterface.ICompilationTarget
import prog8.compilerinterface.IErrorReporter
import prog8.compilerinterface.IVariablesAndConsts
import prog8.compilerinterface.*
internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: CompilationOptions) {
@ -51,7 +48,8 @@ internal fun Program.reorderStatements(errors: IErrorReporter, options: Compilat
internal fun Program.charLiteralsToUByteLiterals(target: ICompilationTarget, errors: IErrorReporter) {
val walker = object : AstWalker() {
override fun after(char: CharLiteral, parent: Node): Iterable<IAstModification> {
if(char.encoding !in target.supportedEncodings) {
require(char.encoding != Encoding.DEFAULT)
if(char.encoding != Encoding.DEFAULT && char.encoding !in target.supportedEncodings) {
errors.err("compilation target doesn't support this text encoding", char.position)
return noModifications
}
@ -83,8 +81,8 @@ internal fun Program.verifyFunctionArgTypes() {
fixer.visit(this)
}
internal fun Program.preprocessAst(errors: IErrorReporter) {
val transforms = AstPreprocessor(this, errors)
internal fun Program.preprocessAst(errors: IErrorReporter, target: ICompilationTarget) {
val transforms = AstPreprocessor(this, errors, target)
transforms.visit(this)
var mods = transforms.applyModifications()
while(mods>0)

View File

@ -155,7 +155,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
else
'_'
}.joinToString("")
call.args[0] = StringLiteral(processed, Encoding.PETSCII, name.position)
call.args[0] = StringLiteral(processed, compTarget.defaultEncoding, name.position)
call.args[0].linkParents(call as Node)
}
}

View File

@ -9,10 +9,24 @@ import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compilerinterface.Encoding
import prog8.compilerinterface.ICompilationTarget
import prog8.compilerinterface.IErrorReporter
class AstPreprocessor(val program: Program, val errors: IErrorReporter) : AstWalker() {
class AstPreprocessor(val program: Program, val errors: IErrorReporter, val compTarget: ICompilationTarget) : AstWalker() {
override fun before(char: CharLiteral, parent: Node): Iterable<IAstModification> {
if(char.encoding==Encoding.DEFAULT)
char.encoding = compTarget.defaultEncoding
return noModifications
}
override fun before(string: StringLiteral, parent: Node): Iterable<IAstModification> {
if(string.encoding==Encoding.DEFAULT)
string.encoding = compTarget.defaultEncoding
return super.before(string, parent)
}
override fun after(range: RangeExpression, parent: Node): Iterable<IAstModification> {
// has to be done before the constant folding, otherwise certain checks there will fail on invalid range sizes
@ -47,7 +61,7 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter) : AstWal
return modifications
}
override fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
// move vardecls in Anonymous scope up to the containing subroutine
// and add initialization assignment in its place if needed

View File

@ -12,6 +12,7 @@ import prog8.ast.statements.VarDecl
import prog8.ast.statements.WhenChoice
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compilerinterface.Encoding
import prog8.compilerinterface.ICompilationTarget
import prog8.compilerinterface.IErrorReporter
@ -21,7 +22,7 @@ internal class LiteralsToAutoVars(private val program: Program,
private val errors: IErrorReporter) : AstWalker() {
override fun after(string: StringLiteral, parent: Node): Iterable<IAstModification> {
if(string.encoding !in target.supportedEncodings) {
if(string.encoding != Encoding.DEFAULT && string.encoding !in target.supportedEncodings) {
errors.err("compilation target doesn't support this text encoding", string.position)
return noModifications
}

View File

@ -68,7 +68,7 @@ class TestAstToSourceText: AnnotationSpec() {
}
""")
val (txt, _) = roundTrip(parseModule(orig))
txt shouldContain Regex("str +s += +petscii:\"fooBar\\\\n\"")
txt shouldContain Regex("str +s += +\"fooBar\\\\n\"")
}
@Test
@ -101,7 +101,7 @@ class TestAstToSourceText: AnnotationSpec() {
}
""")
val (txt, _) = roundTrip(parseModule(orig))
txt shouldContain Regex("ubyte +c += +petscii:'x'")
txt shouldContain Regex("ubyte +c += +'x'")
}
@Test

View File

@ -425,7 +425,7 @@ class TestProg8Parser: FunSpec( {
.statements.filterIsInstance<VarDecl>()[0]
val rhs = decl.value as CharLiteral
rhs.encoding shouldBe Encoding.PETSCII
rhs.encoding shouldBe Encoding.DEFAULT
rhs.value shouldBe 'x'
}
@ -478,7 +478,7 @@ class TestProg8Parser: FunSpec( {
val rhs = decl.value as CharLiteral
rhs.value shouldBe 'x'
rhs.encoding shouldBe Encoding.PETSCII
rhs.encoding shouldBe Encoding.DEFAULT
}
test("on rhs of subroutine-level const decl, screencode encoded") {
@ -532,7 +532,7 @@ class TestProg8Parser: FunSpec( {
.statements.filterIsInstance<Block>()[0]
.statements.filterIsInstance<VarDecl>()[0]
val rhs = decl.value as StringLiteral
rhs.encoding shouldBe Encoding.PETSCII
rhs.encoding shouldBe Encoding.DEFAULT
rhs.value shouldBe "name"
}

View File

@ -52,6 +52,8 @@ internal object DummyCompilationTarget : ICompilationTarget {
override val machine: IMachineDefinition
get() = throw NotImplementedError("dummy")
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES, Encoding.ISO)
override val defaultEncoding = Encoding.PETSCII
override val defaultLauncherType = LauncherType.NONE
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
throw NotImplementedError("dummy")

View File

@ -7,6 +7,7 @@ import prog8.ast.base.VarDeclType
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.compilerinterface.Encoding
/**
@ -293,11 +294,17 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
}
override fun visit(char: CharLiteral) {
output("${char.encoding.prefix}:'${escape(char.value.toString())}'")
if(char.encoding==Encoding.DEFAULT)
output("'${escape(char.value.toString())}'")
else
output("${char.encoding.prefix}:'${escape(char.value.toString())}'")
}
override fun visit(string: StringLiteral) {
output("${string.encoding.prefix}:\"${escape(string.value)}\"")
if(string.encoding==Encoding.DEFAULT)
output("\"${escape(string.value)}\"")
else
output("${string.encoding.prefix}:\"${escape(string.value)}\"")
}
override fun visit(array: ArrayLiteral) {

View File

@ -457,7 +457,7 @@ private fun Prog8ANTLRParser.CharliteralContext.toAst(): CharLiteral {
Encoding.values().singleOrNull { it.prefix == enc }
?: throw SyntaxError("invalid encoding", toPosition())
else
Encoding.PETSCII
Encoding.DEFAULT
return CharLiteral(unescape(text.substring(1, text.length-1), toPosition())[0], encoding, toPosition())
}
@ -469,7 +469,7 @@ private fun Prog8ANTLRParser.StringliteralContext.toAst(): StringLiteral {
Encoding.values().singleOrNull { it.prefix == enc }
?: throw SyntaxError("invalid encoding", toPosition())
else
Encoding.PETSCII
Encoding.DEFAULT
return StringLiteral(unescape(text.substring(1, text.length-1), toPosition()), encoding, toPosition())
}

View File

@ -593,7 +593,7 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
}
class CharLiteral(val value: Char,
val encoding: Encoding,
var encoding: Encoding,
override val position: Position) : Expression() {
override lateinit var parent: Node
@ -607,7 +607,8 @@ class CharLiteral(val value: Char,
throw FatalAstException("can't replace here")
}
override fun copy() = CharLiteral(value, encoding, position)
override fun copy() =
CharLiteral(value, encoding, position)
override fun referencesIdentifier(nameInSource: List<String>) = false
override fun constValue(program: Program): NumericLiteral {
val bytevalue = program.encoding.encodeString(value.toString(), encoding).single()
@ -628,7 +629,7 @@ class CharLiteral(val value: Char,
}
class StringLiteral(val value: String,
val encoding: Encoding,
var encoding: Encoding,
override val position: Position) : Expression() {
override lateinit var parent: Node

View File

@ -1,9 +1,10 @@
package prog8.compilerinterface
enum class Encoding(val prefix: String) {
PETSCII("petscii"), // c64/c128/cx16
SCREENCODES("sc"), // c64/c128/cx16
ISO("iso") // cx16
DEFAULT("default"), // depends on compilation target
PETSCII("petscii"), // c64/c128/cx16
SCREENCODES("sc"), // c64/c128/cx16
ISO("iso") // cx16
}
interface IStringEncoding {

View File

@ -9,6 +9,8 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
val name: String
val machine: IMachineDefinition
val supportedEncodings: Set<Encoding>
val defaultEncoding: Encoding
val defaultLauncherType: LauncherType
override fun encodeString(str: String, encoding: Encoding): List<UByte>
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String

View File

@ -99,7 +99,7 @@ One or more .p8 module files
``-target <compilation target>``
Sets the target output of the compiler, currently 'c64' and 'cx16' are valid targets.
c64 = Commodore 64, c128 = Commodore 128, cx16 = Commander X16.
c64 = Commodore 64, c128 = Commodore 128, cx16 = Commander X16, atari = Atari 800 XL
Default = c64
``-srcdirs <pathlist>``

View File

@ -18,7 +18,8 @@ This CPU is from the late 1970's and early 1980's and was used in many home comp
such as the `Commodore-64 <https://en.wikipedia.org/wiki/Commodore_64>`_.
The language aims to provide many conveniences over raw assembly code (even when using a macro assembler),
while still being low level enough to create high performance programs.
You can compile programs for various machines with this CPU such as the Commodore-64 and Commodore-128, and the Commander X16.
You can compile programs for various machines with this CPU such as the Commodore-64 and Commodore-128,
the Commander X16, and the Atari 800 XL.
Prog8 is copyright © Irmen de Jong (irmen@razorvine.net | http://www.razorvine.net).

View File

@ -12,8 +12,9 @@ Prog8 targets the following hardware:
Currently these machines can be selected as a compilation target (via the ``-target`` compiler argument):
- 'c64': the Commodore 64
- 'c128': the Commodore 128 (*limited support only for now*)
- 'cx16': the `Commander X16 <https://www.commanderx16.com/>`_
- 'c128': the Commodore 128 (*limited support*)
- 'atari': the Atari 800 XL (*experimental support*)
This chapter explains some relevant system details of the c64 and cx16 machines.

View File

@ -3,6 +3,9 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- move memcopy() logic to prog8_lib and call this from sys.memcopy() the same for all targets (so cx16 now also works when kernal is disabled!)
- same for memset()
- get rid of RAW_LOAD_ADDRESS and make specifying the load address for RAW launcher mode required
...
@ -48,6 +51,7 @@ Compiler:
Libraries:
- fix the problems in c128 target, and flesh out its libraries.
- fix the problems in atari target, and flesh out its libraries.
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
- optimize several inner loops in gfx2 even further?
- add modes 2 and 3 to gfx2 (lowres 4 color and 16 color)?

View File

@ -1,33 +1,11 @@
%import textio
%launcher none
main {
sub start() {
ubyte xx = 30
byte cc
uword @shared names_buffer = memory("filenames", 200, 0)
; cc=0
; 30 |> sin8u |> cos8u |> cc
; txt.print_ub(cc)
; txt.nl()
; cc=0
; xx |> sin8u |> cos8u |> cc
; txt.print_ub(cc)
; txt.nl()
100 |> cc
txt.print_b(cc)
txt.nl()
-100 |> abs |> abs |> cc
txt.print_b(cc)
txt.nl()
cc |> abs |> abs |> cc
txt.print_b(cc)
txt.nl()
cc = -100
cc |> abs |> abs |> cc
txt.print_b(cc)
txt.nl()
repeat {
}
do {
ubyte @shared char = '*'
} until true
}
}