added preliminary CommanderX16 machine target support. Fixed nullpointer when importing a missing file.

This commit is contained in:
Irmen de Jong 2020-08-26 01:56:26 +02:00
parent 256781bba5
commit b939562062
14 changed files with 891 additions and 63 deletions

View File

@ -11,10 +11,6 @@
c64utils { c64utils {
const uword ESTACK_LO = $ce00
const uword ESTACK_HI = $cf00
; ----- number conversions to decimal strings ; ----- number conversions to decimal strings
asmsub ubyte2decimal (ubyte value @ A) -> ubyte @ Y, ubyte @ A, ubyte @ X { asmsub ubyte2decimal (ubyte value @ A) -> ubyte @ Y, ubyte @ A, ubyte @ X {
@ -859,7 +855,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits) ; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well) ; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{

View File

@ -0,0 +1,79 @@
; Prog8 definitions for the CommanderX16
; Including memory registers, I/O registers, Basic and Kernal subroutines.
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
; indent format: TABS, size=8
cx16 {
; ---- C64 kernal routines ----
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte address @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(A,X,Y) ; (via 794 ($31A)) open a logical file
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) ; (via 798 ($31E)) define an input channel
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
romsub $FFCF = CHRIN() clobbers(Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock
romsub $FFE1 = STOP() clobbers(A,X) -> ubyte @ Pz, ubyte @ Pc ; (via 808 ($328)) check the STOP key
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @ A ; (via 810 ($32A)) get a character
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use c64scr.plot for a 'safe' wrapper that preserves X.
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
; ---- end of kernal routines ----
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
%asm {{
sei
cld
;lda #%00101111
;sta $00
;lda #%00100111
;sta $01
jsr cx16.IOINIT
jsr cx16.RESTOR
jsr cx16.CINT
lda #0
tax
tay
clc
clv
cli
rts
}}
}
}

View File

@ -0,0 +1,604 @@
; Prog8 definitions for the CommanderX16
; These are the utility subroutines.
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
; indent format: TABS, size=8
%import cx16lib
cx16utils {
; ----- number conversions to decimal strings
asmsub ubyte2decimal (ubyte value @ A) -> ubyte @ Y, ubyte @ A, ubyte @ X {
; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
%asm {{
ldy #uword2decimal.ASCII_0_OFFSET
bne uword2decimal.hex_try200
rts
}}
}
asmsub uword2decimal (uword value @ AY) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- convert 16 bit uword in A/Y to decimal
; output in uword2decimal.decTenThousands, decThousands, decHundreds, decTens, decOnes
; (these are terminated by a zero byte so they can be easily printed)
; also returns Y = 100's, A = 10's, X = 1's
%asm {{
;Convert 16 bit Hex to Decimal (0-65535) Rev 2
;By Omegamatrix Further optimizations by tepples
; routine from http://forums.nesdev.com/viewtopic.php?f=2&t=11341&start=15
;HexToDec99
; start in A
; end with A = 10's, decOnes (also in X)
;HexToDec255
; start in A
; end with Y = 100's, A = 10's, decOnes (also in X)
;HexToDec999
; start with A = high byte, Y = low byte
; end with Y = 100's, A = 10's, decOnes (also in X)
; requires 1 extra temp register on top of decOnes, could combine
; these two if HexToDec65535 was eliminated...
;HexToDec65535
; start with A/Y (low/high) as 16 bit value
; end with decTenThousand, decThousand, Y = 100's, A = 10's, decOnes (also in X)
; (irmen: I store Y and A in decHundreds and decTens too, so all of it can be easily printed)
ASCII_0_OFFSET = $30
temp = P8ZP_SCRATCH_B1 ; byte in zeropage
hexHigh = P8ZP_SCRATCH_W1 ; byte in zeropage
hexLow = P8ZP_SCRATCH_W1+1 ; byte in zeropage
HexToDec65535; SUBROUTINE
sty hexHigh ;3 @9
sta hexLow ;3 @12
tya
tax ;2 @14
lsr a ;2 @16
lsr a ;2 @18 integer divide 1024 (result 0-63)
cpx #$A7 ;2 @20 account for overflow of multiplying 24 from 43,000 ($A7F8) onward,
adc #1 ;2 @22 we can just round it to $A700, and the divide by 1024 is fine...
;at this point we have a number 1-65 that we have to times by 24,
;add to original sum, and Mod 1024 to get a remainder 0-999
sta temp ;3 @25
asl a ;2 @27
adc temp ;3 @30 x3
tay ;2 @32
lsr a ;2 @34
lsr a ;2 @36
lsr a ;2 @38
lsr a ;2 @40
lsr a ;2 @42
tax ;2 @44
tya ;2 @46
asl a ;2 @48
asl a ;2 @50
asl a ;2 @52
clc ;2 @54
adc hexLow ;3 @57
sta hexLow ;3 @60
txa ;2 @62
adc hexHigh ;3 @65
sta hexHigh ;3 @68
ror a ;2 @70
lsr a ;2 @72
tay ;2 @74 integer divide 1,000 (result 0-65)
lsr a ;2 @76 split the 1,000 and 10,000 digit
tax ;2 @78
lda ShiftedBcdTab,x ;4 @82
tax ;2 @84
rol a ;2 @86
and #$0F ;2 @88
ora #ASCII_0_OFFSET
sta decThousands ;3 @91
txa ;2 @93
lsr a ;2 @95
lsr a ;2 @97
lsr a ;2 @99
ora #ASCII_0_OFFSET
sta decTenThousands ;3 @102
lda hexLow ;3 @105
cpy temp ;3 @108
bmi _doSubtract ;2³ @110/111
beq _useZero ;2³ @112/113
adc #23 + 24 ;2 @114
_doSubtract
sbc #23 ;2 @116
sta hexLow ;3 @119
_useZero
lda hexHigh ;3 @122
sbc #0 ;2 @124
Start100s
and #$03 ;2 @126
tax ;2 @128 0,1,2,3
cmp #2 ;2 @130
rol a ;2 @132 0,2,5,7
ora #ASCII_0_OFFSET
tay ;2 @134 Y = Hundreds digit
lda hexLow ;3 @137
adc Mod100Tab,x ;4 @141 adding remainder of 256, 512, and 256+512 (all mod 100)
bcs hex_doSub200 ;2³ @143/144
hex_try200
cmp #200 ;2 @145
bcc hex_try100 ;2³ @147/148
hex_doSub200
iny ;2 @149
iny ;2 @151
sbc #200 ;2 @153
hex_try100
cmp #100 ;2 @155
bcc HexToDec99 ;2³ @157/158
iny ;2 @159
sbc #100 ;2 @161
HexToDec99; SUBROUTINE
lsr a ;2 @163
tax ;2 @165
lda ShiftedBcdTab,x ;4 @169
tax ;2 @171
rol a ;2 @173
and #$0F ;2 @175
ora #ASCII_0_OFFSET
sta decOnes ;3 @178
txa ;2 @180
lsr a ;2 @182
lsr a ;2 @184
lsr a ;2 @186
ora #ASCII_0_OFFSET
; irmen: load X with ones, and store Y and A too, for easy printing afterwards
sty decHundreds
sta decTens
ldx decOnes
rts ;6 @192 Y=hundreds, A = tens digit, X=ones digit
HexToDec999; SUBROUTINE
sty hexLow ;3 @9
jmp Start100s ;3 @12
Mod100Tab
.byte 0,56,12,56+12
ShiftedBcdTab
.byte $00,$01,$02,$03,$04,$08,$09,$0A,$0B,$0C
.byte $10,$11,$12,$13,$14,$18,$19,$1A,$1B,$1C
.byte $20,$21,$22,$23,$24,$28,$29,$2A,$2B,$2C
.byte $30,$31,$32,$33,$34,$38,$39,$3A,$3B,$3C
.byte $40,$41,$42,$43,$44,$48,$49,$4A,$4B,$4C
decTenThousands .byte 0
decThousands .byte 0
decHundreds .byte 0
decTens .byte 0
decOnes .byte 0
.byte 0 ; zero-terminate the decimal output string
}}
}
; ----- utility functions ----
asmsub byte2decimal (byte value @ A) -> ubyte @ Y, ubyte @ A, ubyte @ X {
; ---- A (signed byte) to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
; note: if the number is negative, you have to deal with the '-' yourself!
%asm {{
cmp #0
bpl +
eor #255
clc
adc #1
+ jmp ubyte2decimal
}}
}
asmsub ubyte2hex (ubyte value @ A) -> ubyte @ A, ubyte @ Y {
; ---- A to hex petscii string in AY (first hex char in A, second hex char in Y)
%asm {{
stx P8ZP_SCRATCH_REG_X
pha
and #$0f
tax
ldy _hex_digits,x
pla
lsr a
lsr a
lsr a
lsr a
tax
lda _hex_digits,x
ldx P8ZP_SCRATCH_REG_X
rts
_hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well
}}
}
asmsub uword2hex (uword value @ AY) clobbers(A,Y) {
; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string 'uword2hex.output' (0-terminated)
%asm {{
sta P8ZP_SCRATCH_REG
tya
jsr ubyte2hex
sta output
sty output+1
lda P8ZP_SCRATCH_REG
jsr ubyte2hex
sta output+2
sty output+3
rts
output .text "0000", $00 ; 0-terminated output buffer (to make printing easier)
}}
}
asmsub str2uword(str string @ AY) -> uword @ AY {
; -- returns the unsigned word value of the string number argument in AY
; the number may NOT be preceded by a + sign and may NOT contain spaces
; (any non-digit character will terminate the number string that is parsed)
%asm {{
_result = P8ZP_SCRATCH_W2
sta _mod+1
sty _mod+2
ldy #0
sty _result
sty _result+1
_mod lda $ffff,y ; modified
sec
sbc #48
bpl +
_done ; return result
lda _result
ldy _result+1
rts
+ cmp #10
bcs _done
; add digit to result
pha
jsr _result_times_10
pla
clc
adc _result
sta _result
bcc +
inc _result+1
+ iny
bne _mod
; never reached
_result_times_10 ; (W*4 + W)*2
lda _result+1
sta P8ZP_SCRATCH_REG
lda _result
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
clc
adc _result
sta _result
lda P8ZP_SCRATCH_REG
adc _result+1
asl _result
rol a
sta _result+1
rts
}}
}
asmsub str2word(str string @ AY) -> word @ AY {
; -- returns the signed word value of the string number argument in AY
; the number may be preceded by a + or - sign but may NOT contain spaces
; (any non-digit character will terminate the number string that is parsed)
%asm {{
_result = P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
sty _result
sty _result+1
sty _negative
lda (P8ZP_SCRATCH_W1),y
cmp #'+'
bne +
iny
+ cmp #'-'
bne _parse
inc _negative
iny
_parse lda (P8ZP_SCRATCH_W1),y
sec
sbc #48
bpl _digit
_done ; return result
lda _negative
beq +
sec
lda #0
sbc _result
sta _result
lda #0
sbc _result+1
sta _result+1
+ lda _result
ldy _result+1
rts
_digit cmp #10
bcs _done
; add digit to result
pha
jsr str2uword._result_times_10
pla
clc
adc _result
sta _result
bcc +
inc _result+1
+ iny
bne _parse
; never reached
_negative .byte 0
}}
}
} ; ------ end of block cx16utils
screen {
; ---- this block contains (character) Screen and text I/O related functions ----
asmsub clear_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
; ---- clear the character screen with the given fill character and character color.
; (assumes screen and color matrix are at their default addresses)
%asm {{
brk ; 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 cx16.CHROUT of that single char.
%asm {{
sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG
ldy #0
- lda (P8ZP_SCRATCH_B1),y
beq +
jsr cx16.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_X
jsr cx16utils.ubyte2decimal
pha
tya
jsr cx16.CHROUT
pla
jsr cx16.CHROUT
txa
jsr cx16.CHROUT
ldx P8ZP_SCRATCH_REG_X
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_X
jsr cx16utils.ubyte2decimal
_print_byte_digits
pha
cpy #'0'
beq +
tya
jsr cx16.CHROUT
pla
jsr cx16.CHROUT
jmp _ones
+ pla
cmp #'0'
beq _ones
jsr cx16.CHROUT
_ones txa
jsr cx16.CHROUT
ldx P8ZP_SCRATCH_REG_X
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_X
pha
cmp #0
bpl +
lda #'-'
jsr cx16.CHROUT
+ pla
jsr cx16utils.byte2decimal
jsr print_ub._print_byte_digits
ldx P8ZP_SCRATCH_REG_X
rts
}}
}
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_X
bcc +
pha
lda #'$'
jsr cx16.CHROUT
pla
+ jsr cx16utils.ubyte2hex
jsr cx16.CHROUT
tya
jsr cx16.CHROUT
ldx P8ZP_SCRATCH_REG_X
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_X
sta P8ZP_SCRATCH_B1
bcc +
lda #'%'
jsr cx16.CHROUT
+ ldy #8
- lda #'0'
asl P8ZP_SCRATCH_B1
bcc +
lda #'1'
+ jsr cx16.CHROUT
dey
bne -
ldx P8ZP_SCRATCH_REG_X
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_X
jsr cx16utils.uword2decimal
ldy #0
- lda cx16utils.uword2decimal.decTenThousands,y
beq +
jsr cx16.CHROUT
iny
bne -
+ ldx P8ZP_SCRATCH_REG_X
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_X
jsr cx16utils.uword2decimal
ldx P8ZP_SCRATCH_REG_X
ldy #0
- lda cx16utils.uword2decimal.decTenThousands,y
beq _allzero
cmp #'0'
bne _gotdigit
iny
bne -
_gotdigit
jsr cx16.CHROUT
iny
lda cx16utils.uword2decimal.decTenThousands,y
bne _gotdigit
rts
_allzero
lda #'0'
jmp cx16.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 cx16.CHROUT
tya
eor #255
tay
pla
eor #255
clc
adc #1
bcc +
iny
+ jmp print_uw
}}
}
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
; ---- safe wrapper around PLOT kernel routine, to save the X register.
%asm {{
stx P8ZP_SCRATCH_REG_X
tax
clc
jsr cx16.PLOT
ldx P8ZP_SCRATCH_REG_X
rts
}}
}
} ; ---- end block screen

View File

@ -4,8 +4,6 @@
; ;
; indent format: TABS, size=8 ; indent format: TABS, size=8
%import c64lib
math { math {
%asminclude "library:math.asm", "" %asminclude "library:math.asm", ""
} }

View File

@ -4,8 +4,6 @@
; ;
; indent format: TABS, size=8 ; indent format: TABS, size=8
%import c64lib
prog8_lib { prog8_lib {
%asminclude "library:prog8lib.asm", "" %asminclude "library:prog8lib.asm", ""
} }

View File

@ -8,6 +8,7 @@ import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.c64.C64MachineDefinition import prog8.compiler.target.c64.C64MachineDefinition
import prog8.compiler.target.c64.Petscii import prog8.compiler.target.c64.Petscii
import prog8.compiler.target.c64.codegen.AsmGen import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.target.cx16.CX16MachineDefinition
import prog8.parser.ParsingFailedError import prog8.parser.ParsingFailedError
import java.io.IOException import java.io.IOException
import java.nio.file.FileSystems import java.nio.file.FileSystems
@ -52,7 +53,7 @@ private fun compileMain(args: Array<String>) {
when(compilationTarget) { when(compilationTarget) {
"c64" -> { "c64" -> {
with(CompilationTarget) { with(CompilationTarget) {
name = "c64" name = "Commodore-64"
machine = C64MachineDefinition machine = C64MachineDefinition
encodeString = { str, altEncoding -> encodeString = { str, altEncoding ->
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true) if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
@ -63,8 +64,21 @@ private fun compileMain(args: Array<String>) {
asmGenerator = ::AsmGen asmGenerator = ::AsmGen
} }
} }
"cx16" -> {
with(CompilationTarget) {
name = "Commander X16"
machine = CX16MachineDefinition
encodeString = { str, altEncoding ->
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
}
decodeString = { bytes, altEncoding ->
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
}
asmGenerator = ::AsmGen
}
}
else -> { else -> {
System.err.println("invalid compilation target") System.err.println("invalid compilation target. Available are: c64, cx16")
exitProcess(1) exitProcess(1)
} }
} }
@ -121,20 +135,7 @@ private fun compileMain(args: Array<String>) {
if (compilationResult.programName.isEmpty()) if (compilationResult.programName.isEmpty())
println("\nCan't start emulator because no program was assembled.") println("\nCan't start emulator because no program was assembled.")
else if(startEmulator) { else if(startEmulator) {
for(emulator in listOf("x64sc", "x64")) { CompilationTarget.machine.launchEmulator(compilationResult.programName)
println("\nStarting C-64 emulator $emulator...")
val cmdline = listOf(emulator, "-silent", "-moncommands", "${compilationResult.programName}.vice-mon-list",
"-autostartprgmode", "1", "-autostart-warp", "-autostart", compilationResult.programName + ".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
}
} }
} }
} }

View File

@ -90,11 +90,8 @@ private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program,
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG) if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type PRG.") throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type PRG.")
// if we're producing a PRG or BASIC program, include the c64utils and c64lib libraries // depending on the mach9ine and compiler options we may have to include some libraries
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG) { CompilationTarget.machine.importLibs(compilerOptions, importer, programAst)
importer.importLibraryModule(programAst, "c64lib")
importer.importLibraryModule(programAst, "c64utils")
}
// always import prog8lib and math // always import prog8lib and math
importer.importLibraryModule(programAst, "math") importer.importLibraryModule(programAst, "math")

View File

@ -1,15 +1,17 @@
package prog8.compiler.target package prog8.compiler.target
import prog8.ast.Program
import prog8.compiler.CompilationOptions import prog8.compiler.CompilationOptions
import prog8.compiler.Zeropage import prog8.compiler.Zeropage
import prog8.parser.ModuleImporter
interface IMachineFloat { internal interface IMachineFloat {
fun toDouble(): Double fun toDouble(): Double
fun makeFloatFillAsm(): String fun makeFloatFillAsm(): String
} }
interface IMachineDefinition { internal interface IMachineDefinition {
val FLOAT_MAX_NEGATIVE: Double val FLOAT_MAX_NEGATIVE: Double
val FLOAT_MAX_POSITIVE: Double val FLOAT_MAX_POSITIVE: Double
val FLOAT_MEM_SIZE: Int val FLOAT_MEM_SIZE: Int
@ -22,8 +24,11 @@ interface IMachineDefinition {
val opcodeNames: Set<String> val opcodeNames: Set<String>
var zeropage: Zeropage var zeropage: Zeropage
val initSystemProcname: String val initSystemProcname: String
val cpu: String
fun initializeZeropage(compilerOptions: CompilationOptions) fun initializeZeropage(compilerOptions: CompilationOptions)
fun getFloat(num: Number): IMachineFloat fun getFloat(num: Number): IMachineFloat
fun getFloatRomConst(number: Double): String? fun getFloatRomConst(number: Double): String?
fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program)
fun launchEmulator(programName: String)
} }

View File

@ -22,7 +22,7 @@ class AssemblyProgram(override val name: String, outputDir: Path) : IAssemblyPro
val outFile = when (options.output) { val outFile = when (options.output) {
OutputType.PRG -> { OutputType.PRG -> {
command.add("--cbm-prg") command.add("--cbm-prg")
println("\nCreating C-64 prg.") println("\nCreating prg.")
prgFile prgFile
} }
OutputType.RAW -> { OutputType.RAW -> {

View File

@ -1,17 +1,19 @@
package prog8.compiler.target.c64 package prog8.compiler.target.c64
import prog8.compiler.CompilationOptions import prog8.ast.Program
import prog8.compiler.CompilerException import prog8.compiler.*
import prog8.compiler.Zeropage
import prog8.compiler.ZeropageType
import prog8.compiler.target.IMachineDefinition import prog8.compiler.target.IMachineDefinition
import prog8.compiler.target.IMachineFloat import prog8.compiler.target.IMachineFloat
import prog8.parser.ModuleImporter
import java.io.IOException
import java.math.RoundingMode import java.math.RoundingMode
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.math.pow import kotlin.math.pow
internal object C64MachineDefinition: IMachineDefinition { internal object C64MachineDefinition: IMachineDefinition {
override val cpu = "6502"
// 5-byte cbm MFLPT format limitations: // 5-byte cbm MFLPT format limitations:
override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255 override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
override val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255 override val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
@ -64,6 +66,31 @@ internal object C64MachineDefinition: IMachineDefinition {
return null return null
} }
override fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program) {
// if we're producing a PRG or BASIC program, include the c64utils and c64lib libraries
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG) {
importer.importLibraryModule(program, "c64lib")
importer.importLibraryModule(program, "c64utils")
}
}
override fun launchEmulator(programName: String) {
for(emulator in listOf("x64sc", "x64")) {
println("\nStarting C-64 emulator $emulator...")
val cmdline = listOf(emulator, "-silent", "-moncommands", "$programName.vice-mon-list",
"-autostartprgmode", "1", "-autostart-warp", "-autostart", programName + ".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 initializeZeropage(compilerOptions: CompilationOptions) { override fun initializeZeropage(compilerOptions: CompilationOptions) {
zeropage = C64Zeropage(compilerOptions) zeropage = C64Zeropage(compilerOptions)
} }
@ -143,11 +170,11 @@ internal object C64MachineDefinition: IMachineDefinition {
free.clear() free.clear()
} }
} }
assert(SCRATCH_B1 !in free) require(SCRATCH_B1 !in free)
assert(SCRATCH_REG !in free) require(SCRATCH_REG !in free)
assert(SCRATCH_REG_X !in free) require(SCRATCH_REG_X !in free)
assert(SCRATCH_W1 !in free) require(SCRATCH_W1 !in free)
assert(SCRATCH_W2 !in free) require(SCRATCH_W2 !in free)
for (reserved in options.zpReserved) for (reserved in options.zpReserved)
reserve(reserved) reserve(reserved)

View File

@ -78,11 +78,13 @@ internal class AsmGen(private val program: Program,
private fun header() { private fun header() {
val ourName = this.javaClass.name val ourName = this.javaClass.name
out("; 6502 assembly code for '${program.name}'") val cpu = CompilationTarget.machine.cpu
out("; $cpu assembly code for '${program.name}'")
out("; generated by $ourName on ${LocalDateTime.now().withNano(0)}") out("; generated by $ourName on ${LocalDateTime.now().withNano(0)}")
out("; assembler syntax is for the 64tasm cross-assembler") out("; assembler syntax is for the 64tasm cross-assembler")
out("; output options: output=${options.output} launcher=${options.launcher} zp=${options.zeropage}") out("; output options: output=${options.output} launcher=${options.launcher} zp=${options.zeropage}")
out("\n.cpu '6502'\n.enc 'none'\n") out("\n.cpu '$cpu'\n.enc 'none'\n")
program.actualLoadAddress = program.definedLoadAddress program.actualLoadAddress = program.definedLoadAddress
if (program.actualLoadAddress == 0) // fix load address if (program.actualLoadAddress == 0) // fix load address

View File

@ -0,0 +1,125 @@
package prog8.compiler.target.cx16
import prog8.ast.Program
import prog8.compiler.*
import prog8.compiler.target.IMachineDefinition
import prog8.compiler.target.c64.C64MachineDefinition
import prog8.parser.ModuleImporter
import java.io.IOException
internal object CX16MachineDefinition: IMachineDefinition {
override val cpu = "65c02"
// 5-byte cbm MFLPT format limitations:
override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
override val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
override val FLOAT_MEM_SIZE = 5
override val POINTER_MEM_SIZE = 2
override val BASIC_LOAD_ADDRESS = 0x0801
override val RAW_LOAD_ADDRESS = 0x8000
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
// and some heavily used string constants derived from the two values above
override val ESTACK_LO = 0x0400 // $0400-$04ff inclusive
override val ESTACK_HI = 0x0500 // $0500-$05ff inclusive
override lateinit var zeropage: Zeropage
override val initSystemProcname = "cx16.init_system"
override fun getFloat(num: Number) = C64MachineDefinition.Mflpt5.fromNumber(num)
override fun getFloatRomConst(number: Double): String? = null // TODO Does Cx16 have ROM float locations?
override fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program) {
// if we're producing a PRG or BASIC program, include the cx16utils and cx16lib libraries
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG) {
importer.importLibraryModule(program, "cx16lib")
importer.importLibraryModule(program, "cx16utils")
}
}
override fun launchEmulator(programName: String) {
for(emulator in listOf("x16emu")) {
println("\nStarting Commander X16 emulator $emulator...")
val cmdline = listOf(emulator, "-rom", "/usr/share/x16-rom/rom.bin", "-scale", "2",
"-run", "-prg", programName + ".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 initializeZeropage(compilerOptions: CompilationOptions) {
zeropage = CX16Zeropage(compilerOptions)
}
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
// TODO add 65C02 opcodes
override val opcodeNames = 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")
internal class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x79 // temp storage for a single byte
override val SCRATCH_REG = 0x7a // temp storage for a register
override val SCRATCH_REG_X = 0x7b // temp storage for register X (the evaluation stack pointer)
override val SCRATCH_W1 = 0x7c // temp storage 1 for a word $7c+$7d
override val SCRATCH_W2 = 0x7e // temp storage 2 for a word $7e+$7f
override val exitProgramStrategy: ExitProgramStrategy = when (options.zeropage) {
ZeropageType.BASICSAFE, ZeropageType.DONTUSE -> ExitProgramStrategy.CLEAN_EXIT
ZeropageType.KERNALSAFE, ZeropageType.FULL -> ExitProgramStrategy.SYSTEM_RESET
else -> ExitProgramStrategy.SYSTEM_RESET
}
init {
if (options.floats && options.zeropage !in setOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
throw CompilerException("when floats are enabled, zero page type should be 'basicsafe' or 'dontuse'")
when (options.zeropage) {
ZeropageType.FULL -> {
free.addAll(0x02..0xff)
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_REG_X, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
}
ZeropageType.KERNALSAFE -> {
free.addAll(0x02..0x7f)
free.addAll(0xa9..0xff)
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_REG_X, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
}
ZeropageType.BASICSAFE -> {
free.addAll(0x02..0x7f)
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_REG_X, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
}
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
else -> throw CompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
}
require(SCRATCH_B1 !in free)
require(SCRATCH_REG !in free)
require(SCRATCH_REG_X !in free)
require(SCRATCH_W1 !in free)
require(SCRATCH_W2 !in free)
for (reserved in options.zpReserved)
reserve(reserved)
}
}
}

View File

@ -94,7 +94,7 @@ internal class ModuleImporter {
private fun discoverImportedModuleFile(name: String, source: Path, position: Position?): Path { private fun discoverImportedModuleFile(name: String, source: Path, position: Position?): Path {
val fileName = "$name.p8" val fileName = "$name.p8"
val locations = mutableListOf(source.parent) val locations = if(source.toString().isEmpty()) mutableListOf<Path>() else mutableListOf(source.parent)
val propPath = System.getProperty("prog8.libdir") val propPath = System.getProperty("prog8.libdir")
if(propPath!=null) if(propPath!=null)
@ -109,7 +109,7 @@ internal class ModuleImporter {
if (Files.isReadable(file)) return file if (Files.isReadable(file)) return file
} }
throw ParsingFailedError("$position Import: no module source file '$fileName' found (I've looked in: $locations)") throw ParsingFailedError("$position Import: no module source file '$fileName' found (I've looked in: embedded libs and $locations)")
} }
private fun executeImportDirective(program: Program, import: Directive, source: Path): Module? { private fun executeImportDirective(program: Program, import: Directive, source: Path): Module? {
@ -128,10 +128,7 @@ internal class ModuleImporter {
if(resource!=null) { if(resource!=null) {
// load the module from the embedded resource // load the module from the embedded resource
resource.use { resource.use {
if(import.args[0].int==42) println("importing '$moduleName' (library)")
println("importing '$moduleName' (library, auto)")
else
println("importing '$moduleName' (library)")
importModule(program, CharStreams.fromStream(it), Paths.get("@embedded@/$moduleName"), true) importModule(program, CharStreams.fromStream(it), Paths.get("@embedded@/$moduleName"), true)
} }
} else { } else {

View File

@ -1,4 +1,3 @@
%import c64utils
%zeropage basicsafe %zeropage basicsafe
main { main {
@ -8,25 +7,25 @@ main {
uword xx = $ef34 uword xx = $ef34
xx &= $00f0 xx &= $00f0
c64scr.print_uwhex(xx, 1) screen.print_uwhex(xx, 1)
c64.CHROUT('\n') cx16.CHROUT('\n')
xx |= $000f xx |= $000f
c64scr.print_uwhex(xx, 1) screen.print_uwhex(xx, 1)
c64.CHROUT('\n') cx16.CHROUT('\n')
xx ^= $0011 xx ^= $0011
c64scr.print_uwhex(xx, 1) screen.print_uwhex(xx, 1)
c64.CHROUT('\n') cx16.CHROUT('\n')
xx = $ef34 xx = $ef34
xx &= $f000 xx &= $f000
c64scr.print_uwhex(xx, 1) screen.print_uwhex(xx, 1)
c64.CHROUT('\n') cx16.CHROUT('\n')
xx |= $0f00 xx |= $0f00
c64scr.print_uwhex(xx, 1) screen.print_uwhex(xx, 1)
c64.CHROUT('\n') cx16.CHROUT('\n')
xx ^= $1100 xx ^= $1100
c64scr.print_uwhex(xx, 1) screen.print_uwhex(xx, 1)
c64.CHROUT('\n') cx16.CHROUT('\n')
} }
} }