Compare commits

...

5 Commits

Author SHA1 Message Date
1b420f7fe7 Add a preliminary external custom target for the Foenix F256 family of modern retro computers. (#171) 2025-06-29 11:14:34 +02:00
6a9a82ff9d doc 2025-06-27 18:14:36 +02:00
aa36e6b19f flesh out C128-specific KERNAL calls (#170)
* flesh out C128-specific KERNAL calls

* fix: typo in comment

* fix: typo in comment

* fix: include return values of INDCMP

* fix: rearrange return values of INDCMP
2025-06-27 18:13:26 +02:00
51cb6aad50 add c128.PRIMM() 2025-06-27 17:39:31 +02:00
b5ce409592 clarify booleans 2025-06-26 20:06:36 +02:00
12 changed files with 1544 additions and 22 deletions

View File

@ -96,7 +96,7 @@ What does Prog8 provide?
- "c64": Commodore-64 (6502 like CPU)
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
- "pet32": Commodore PET (limited support)
- via external configurable targets: Atari 800 XL, Neo6502, NES, C64OS, ...
- via external configurable targets: Atari 800 XL, Neo6502, NES, C64 OS, Foenix F256, ...
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag)

View File

@ -317,10 +317,25 @@ c128 {
&ubyte VM4 = $0A2F ; starting page for VDC attribute mem
; TODO c128 a bunch of kernal routines are missing here that are specific to the c128
extsub $FF6E = JSRFAR()
extsub $FF68 = SETBNK(ubyte databank @A, ubyte filenamebank @X)
extsub $FF47 = SPIN_SPOUT() clobbers(A) ; set up serial bus for fast communications mode
extsub $FF4A = CLOSE_ALL(ubyte device @X) clobbers(X) ; close all channels to specific device
extsub $FF4D = C64_MODE() ; restart machine in C64 mode (does not return)
extsub $FF50 = DMA_CALL(ubyte bank @X, ubyte command @Y) clobbers(A,X) ; send a command to a DMA device
extsub $FF53 = BOOT_CALL(ubyte device @X, ubyte drive @A) clobbers(A,X,Y) ; try to autoboot the given disk
extsub $FF56 = PHOENIX() clobbers(A,X,Y) ; search for and autostart ROMs, cartridges, then default disk
extsub $FF59 = LKUPLA(ubyte lfn @A) -> bool @Pc, ubyte @X ; look up logical file number to see if it's open; returns device
extsub $FF5C = LKUPSA(ubyte sa @Y) -> bool @Pc, ubyte @A, ubyte @X ; look up secondary address to see if it's in use; returns lfn and device
extsub $FF5F = SWAPPER() clobbers(A,X,Y) ; swap active screen (between 40- and 80-column)
extsub $FF62 = DLCHR() clobbers(A,X,Y) ; copy character ROM into VDC video RAM
extsub $FF65 = PFKEY(ubyte zpaddr @A, ubyte key @X, ubyte length @Y) ; redefine programmable function key (string descriptor in zp, addr in A)
extsub $FF68 = SETBNK(ubyte data_bank @A, ubyte filename_bank @X) ; set memory bank for load/save
extsub $FF6B = GETCFG(ubyte bank @X) -> ubyte @A ; translate bank number to MMU configuration register value
extsub $FF6E = JSRFAR() clobbers(A,X) ; call routine in another bank (parameters set in zero page addresses 2-8)
extsub $FF71 = JMPFAR() clobbers(A,X) ; jump without return to another bank (parameters set as for JSRFAR)
extsub $FF74 = INDFET(ubyte zpaddr @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> ubyte @A ; fetch byte from another bank (address in zp, ptr in A)
extsub $FF77 = INDSTA(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) ; store byte to another bank (address in zp, ptr in $02b9)
extsub $FF7A = INDCMP(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> bool @Pz, bool @Pc, bool @Pv; compare byte in another bank (address in zp, ptr in $02c8)
extsub $FF7D = PRIMM() ; print immediate string
; ---- C128 specific system utility routines: ----

View File

@ -494,8 +494,25 @@ c128 {
&ubyte VM2
&ubyte VM3
&ubyte VM4
JSRFAR () = $ff6e
SETBNK (ubyte databank @A, ubyte filenamebank @X) = $ff68
BOOT_CALL (ubyte device @X, ubyte drive @A) -> clobbers (A,X,Y) = $ff53
C64_MODE () = $ff4d
CLOSE_ALL (ubyte device @X) -> clobbers (X) = $ff4a
DLCHR () -> clobbers (A,X,Y) = $ff62
DMA_CALL (ubyte bank @X, ubyte command @Y) -> clobbers (A,X) = $ff50
GETCFG (ubyte bank @X) -> ubyte @A = $ff6b
INDCMP (ubyte value @A, ubyte bank @X, ubyte offset @Y) -> clobbers (X) -> bool @Pz, bool @Pc, bool @Pv = $ff7a
INDFET (ubyte zpaddr @A, ubyte bank @X, ubyte offset @Y) -> clobbers (X) -> ubyte @A = $ff74
INDSTA (ubyte value @A, ubyte bank @X, ubyte offset @Y) -> clobbers (X) = $ff77
JMPFAR () -> clobbers (A,X) = $ff71
JSRFAR () -> clobbers (A,X) = $ff6e
LKUPLA (ubyte lfn @A) -> bool @Pc, ubyte @X = $ff59
LKUPSA (ubyte sa @Y) -> bool @Pc, ubyte @A, ubyte @X = $ff5c
PFKEY (ubyte zpaddr @A, ubyte key @X, ubyte length @Y) = $ff65
PHOENIX () -> clobbers (A,X,Y) = $ff56
PRIMM () = $ff7d
SETBNK (ubyte data_bank @A, ubyte filename_bank @X) = $ff68
SPIN_SPOUT () -> clobbers (A) = $ff47
SWAPPER () -> clobbers (A,X,Y) = $ff5f
banks (ubyte banks @A)
disable_basic () -> clobbers (A)
getbanks () -> ubyte @A

View File

@ -27,8 +27,8 @@ You can compile programs for various machines that are built in into the compile
* Commodore 128 (limited support)
* Commodore PET (limited support)
* any other 65(C)02 target machine or setup can be configured to a great extent in a user written configuration file.
There are some examples included for the Atari 800 XL, NEO6502 and such.
* some users have been experimenting with a NES and a C64OS target as well.
There are some examples included for the Atari 800 XL, NEO6502, Foenix F256, and such.
* some users have been experimenting with a NES and a C64 OS target as well.
Some language features are mentioned below, and you can also read :ref:`comparingprog8` if you
want to quickly read about how Prog8 compares to well-known other languages.

View File

@ -1032,6 +1032,7 @@ sys (part of syslib)
- 7 = Neo6502
- 8 = Atari 8 bits
- 16 = Commander X16
- 25 = Foenix F256 family
- 64 = Commodore 64
- 128 = Commodore 128
- 255 = Virtual machine

View File

@ -244,16 +244,9 @@ you have to make sure the resulting value fits into the byte or word size of the
Booleans
^^^^^^^^
Booleans are a distinct type in Prog8 and can have only the values ``true`` or ``false``.
It can be casted to and from other integer types though
where a nonzero integer is considered to be true, and zero is false.
Logical expressions, comparisons and some other code tends to compile more efficiently if
you explicitly use ``bool`` types instead of 0/1 integers.
The in-memory representation of a boolean value is just a byte containing 0 or 1.
If you find that you need a whole bunch of boolean variables or perhaps even an array of them,
consider using integer bit mask variable + bitwise operators instead.
This saves a lot of memory and may be faster as well.
Booleans are a distinct type called ``bool`` in Prog8 and can have only the values ``true`` or ``false``.
In memory, they are stored as a byte containing 0 or 1.
You can cast any numeric to a bool, in which case 0 will become ``false`` and any nonzero value will become ``true``.
Floating point numbers

View File

@ -1,6 +1,6 @@
.PHONY: all clean
all: main-c64.prg main-cx16.prg main-pet.prg atari-hello.xex atari-fibonacci.xex neo-hello.bin
all: main-c64.prg main-cx16.prg main-pet.prg atari-hello.xex atari-fibonacci.xex neo-hello.bin f256-hello.bin
clean:
rm -f *.prg *.PRG *.xex *.bin *.asm *.vice-*
@ -26,6 +26,9 @@ atari-fibonacci.xex: src/atari-fibonacci.p8
neo-hello.bin: src/neo-hello.p8
prog8c -target targetconfigs/neo6502.properties src/neo-hello.p8
f256-hello.bin: src/f256-hello.p8
prog8c -target targetconfigs/f256.properties src/f256-hello.p8
run-atari-hello:
prog8c -target targetconfigs/atari.properties src/atari-hello.p8 -emu

View File

@ -0,0 +1,907 @@
; Prog8 definitions for the Foenix F256x
%option no_symbol_prefixing, ignore_unused
; compatiblity layer
cbm {
%option no_symbol_prefixing, ignore_unused
; dumb hack to make some code compile.
; TODO: figure out how the kernel clock works, not just
; reading the RTC hardware
ubyte TIME_LO = $00
;
; Only reads keys right now.
;
asmsub CHRIN() -> ubyte @A {
%asm {{
- stz f256.event.type ; invalidate existing event type
jsr f256.event.NextEvent
lda f256.event.type
cmp #f256.event.key.PRESSED
bne -
lda f256.event.key.ascii ; return upper or lower ASCII
rts
}}
}
; I though the alias was working but it isn't.
;alias CHROUT = f256.chrout
asmsub CHROUT(ubyte character @ A) {
%asm {{
jmp f256.chrout
}}
}
; TODO: set carry properly?
; TODO: on CBM any device other than keyboard or RS-232 should jump to CHRIN
asmsub GETIN() -> bool @Pc, ubyte @A {
%asm {{
- stz f256.event.type ; invalidate previous event type
lda f256.event.pending ; return zero if no pending event
cmp #$ff ; $ff == no events pending
beq + ; done.
jsr f256.event.NextEvent
lda f256.event.type
cmp #f256.event.key.PRESSED
bne -
;lda f256.event.key.raw ; return key id / scan code? (case issue?)
lda f256.event.key.ascii ; return upper or lower ASCII
clc ; clear carry when returning character
rts ; return ascii key
+ sec ; set carry when no character returned
lda #0 ; return zero if no key
rts
}}
}
asmsub GETINx() -> bool @Pc, ubyte @A {
%asm {{
rts
}}
}
}
f256 {
%option no_symbol_prefixing, ignore_unused, force_output
;
; Foenix F256 hardware definitions
;
; MMU controls
&ubyte mem_ctrl = $0000
&ubyte io_ctrl = $0001
; text screen size
const ubyte DEFAULT_WIDTH = 80
const ubyte DEFAULT_HEIGHT = 60
; screen / color memory
const uword Colors = $C000 ; IO page 2
const uword Screen = $C000 ; IO page 3
; self tracked screen coordinates
; potentially could be at $04/$05 in reserved ZP area?
ubyte screen_row = 0
ubyte screen_col = 0
ubyte screen_color = $f2 ; default text/background color
&uword screen_ptr = $02 ; and $03. used to calculate screen/color ram offsets
;
; calculates screen memory pointer for the start of a row
; in screen_ptr in zeropage.
; ldy column
; sta (screen_ptr), y
;
asmsub rowptr(ubyte row @Y) {
%asm {{
stz screen_ptr ; reset to start of screen ram
lda #>f256.Screen
sta screen_ptr+1
cpy #0 ; row in @Y will be our loop counter
beq ptr_done
rowloop:
clc
lda screen_ptr ; load count
adc #DEFAULT_WIDTH
bcc +
inc screen_ptr+1
+ sta screen_ptr
dey
bne rowloop
ptr_done:
rts
}}
}
;
; calculates screen memory pointer for the specific col/row
; in screen_ptr in zeropage. Points directly to character after.
; ldy #0
; sta (screen_ptr), y
;
asmsub chrptr(ubyte col @X, ubyte row @Y) clobbers(A) {
%asm {{
phx ; preserve col
jsr rowptr ; calculate pointer to row
pla ; restore col
clc
adc screen_ptr
sta screen_ptr
bcc +
inc screen_ptr+1
+ rts
}}
}
asmsub chrout(ubyte character @ A) {
%asm {{
phx ; preserve x
phy ; preserve y
cmp #$0d ; check for carriage return
beq crlf
cmp #$0a ; check for line feed
beq crlf
pha ; preserve a
ldy screen_row
jsr rowptr ; calculates screen pointer to start of row
ldy screen_col ; column will be our index against the row pointer
lda #2
sta f256.io_ctrl ; map in screen memory
pla
sta (screen_ptr),y
lda #3
sta f256.io_ctrl ; map in color memory
lda screen_color
sta (screen_ptr),y
lda #0
sta f256.io_ctrl ; return to default map
inc screen_col
lda screen_col
cmp #DEFAULT_WIDTH
bcc + ; less than DEFAULT_WIDTH
crlf:
stz screen_col
inc screen_row
lda screen_row
cmp #DEFAULT_HEIGHT
bcc +
sec
jsr scroll_up
dec screen_row
+ ply
plx
rts
}}
}
;
; implement here so chrout can work without importing textio.
;
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A,X) {
; ---- 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
%asm {{
bcc _scroll_screen
+ ; scroll the screen and the color memory
ldx #DEFAULT_WIDTH-1
-
lda #2
sta f256.io_ctrl ; map in screen memory
.for row=1, row<=DEFAULT_HEIGHT, row+=1
lda f256.Screen + DEFAULT_WIDTH*row,x
sta f256.Screen + DEFAULT_WIDTH*(row-1),x
.next
lda #3
sta f256.io_ctrl ; map in color memory
.for row=1, row<=DEFAULT_HEIGHT, row+=1
lda f256.Colors + DEFAULT_WIDTH*row,x
sta f256.Colors + DEFAULT_WIDTH*(row-1),x
.next
dex
bpl -
lda #0
sta f256.io_ctrl ; restore I/O configuration
rts
_scroll_screen ; scroll only the screen memory
ldx #DEFAULT_WIDTH-1
-
lda #2
sta f256.io_ctrl ; map in screen memory
.for row=1, row<=DEFAULT_HEIGHT, row+=1
lda f256.Screen + DEFAULT_WIDTH*row,x
sta f256.Screen + DEFAULT_WIDTH*(row-1),x
.next
dex
bpl -
lda #0
sta f256.io_ctrl ; restore I/O configuration
rts
}}
}
; args
sub args() {
&uword ext = $00f8
&ubyte extlen = $00fa
&uword buf = $00fb
&ubyte buflen = $00fd
&uword ptr = $00fe
}
; kernel event calls & event types
sub event() {
&uword dest = $00f0
&ubyte pending = $00f2
&ubyte[8] packet = $00e8 ; 8-byte buffer in ZP for kernel events.
; event buffer fields
&ubyte type = &packet ; event type (definitions below)
&ubyte buf = &packet+1 ; page id or zero
&ubyte ext = &packet+2 ; page id or zero
; kernel event calls
extsub $ff00 = NextEvent() ; Copy next event to application buffer.
extsub $ff04 = ReadData() ; Copy primary bulk event data to application.
extsub $ff08 = ReadExt() ; Copy secondary bulk event data to application.
sub init() {
f256.event.dest = &f256.event.packet
}
; event type definitions
const uword reserved = $00 ; $01
const uword deprecated = $02 ; $03
const uword JOYSTICK = $04 ; $05 ; game controller changes
const uword DEVICE = $06 ; $07 ; device added or removed
sub key() {
const uword PRESSED = $08 ; $09
const uword RELEASED = $0a ; $0b
&ubyte keyboard = &packet+3 ; keyboard id
&ubyte raw = &packet+4 ; raw key id/code
&ubyte ascii = &packet+5 ; ASCII value
&ubyte flags = &packet+6 ; flag (META)
const ubyte META = $80 ; meta key no ASCII value
}
sub mouse() {
const uword DELTA = $0c ; $0d
const uword CLICKS = $0e ; $0f
}
sub block() {
const uword NAME = $10 ; $11
const uword SIZE = $12 ; $13
const uword DATA = $14 ; $15 ; read request succeeded
const uword WROTE = $16 ; $17 ; write request completed
const uword FORMATTED = $18 ; $19 ; low-level format completed
const uword ERROR = $1a ; $1b
}
sub fs() {
const uword SIZE = $1c ; $1d
const uword CREATED = $1e ; $1f
const uword CHECKED = $20 ; $21
const uword DATA = $22 ; $23 ; read request succeeded
const uword WROTE = $24 ; $25 ; write request completed
const uword ERROR = $26 ; $27
}
sub file() {
const uword NOT_FOUND = $28 ; $29 ; file was not found
const uword OPENED = $2a ; $2b ; file successfully opened
const uword DATA = $2c ; $2d ; read request succeeded
const uword WROTE = $2e ; $2f ; write request completed
const uword EOF = $30 ; $31 ; all file data has been read
const uword CLOSED = $32 ; $33 ; close request completed
const uword RENAMED = $34 ; $35 ; renamed request completed
const uword DELETED = $36 ; $37 ; delete request completed
const uword ERROR = $38 ; $39 ; error occurred, close file if opened
const uword SEEK = $3a ; $3b ; seek request completed
}
sub directory() {
const uword OPENED = $3c ; $3d ; directory open succeeded
const uword VOLUME = $3e ; $3f ; volume record found
const uword FILE = $40 ; $41 ; file record found
const uword FREE = $42 ; $43 ; file-system free-space record found
const uword EOF = $44 ; $45 ; all data read
const uword CLOSED = $46 ; $47 ; directory file closed
const uword ERROR = $48 ; $49 ; error occurred, close if opened
const uword CREATED = $4a ; $4b ; directory created
const uword DELETED = $4a ; $4b ; directory deleted
}
sub net() {
const uword TCP = $4c ; $4d
const uword UDP = $4e ; $4f
}
sub timer() {
const uword EXPIRED = $50 ; $51
}
sub clock() {
const uword TICK = $52 ; $53
}
sub irq() {
const uword IRQ = $54 ; $55
}
}
; kernel display calls
sub display() {
&ubyte x = $00f3
&ubyte y = $00f4
&uword color = &f256.args.ext
&uword text = &f256.args.buf
&ubyte buflen = &f256.args.buflen
const uword GetSize_ = $ffd0 ; Get screen dimensions
const uword DrawRow_ = $ffd4 ; Draw text/color buffers left-to-right
const uword DrawColumn_ = $ffd8 ; Draw text/color buffers top-to-bottom
}
;
&uword NMI_VEC = $FFFA ; 6502 nmi vector
&uword RESET_VEC = $FFFC ; 6502 reset vector
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector
;%asminclude "api.asm"
}
sys {
; ------- lowlevel system routines --------
const ubyte target = 25 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 25=Foenix F256, 16=CommanderX16, 8=atari800XL, 7=Neo6502
const ubyte SIZEOF_BOOL = sizeof(bool)
const ubyte SIZEOF_BYTE = sizeof(byte)
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
const ubyte SIZEOF_WORD = sizeof(word)
const ubyte SIZEOF_UWORD = sizeof(uword)
const ubyte SIZEOF_LONG = sizeof(long)
; const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
const ubyte SIZEOF_FLOAT = 0 ; undefined, no floats supported
const byte MIN_BYTE = -128
const byte MAX_BYTE = 127
const ubyte MIN_UBYTE = 0
const ubyte MAX_UBYTE = 255
const word MIN_WORD = -32768
const word MAX_WORD = 32767
const uword MIN_UWORD = 0
const uword MAX_UWORD = 65535
; MIN_FLOAT and MAX_FLOAT are defined in the floats module if importec
asmsub reset_system() {
; Soft-reset the system back to initial power-on status
; TODO
%asm {{
sei
jmp (f256.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 internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
; Called when the compiler wants to assign a string value to another string.
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda cx16.r0
ldy cx16.r0+1
jmp prog8_lib.strcpy
}}
}
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
; note: only works for NON-OVERLAPPING memory regions!
; 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
dey
beq +
- lda (P8ZP_SCRATCH_W1),y
sta (P8ZP_SCRATCH_W2),y
dey
bne -
+ lda (P8ZP_SCRATCH_W1),y
sta (P8ZP_SCRATCH_W2),y
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 irqsafe_set_irqd() {
%asm {{
php
sei
}}
}
inline asmsub irqsafe_clear_irqd() {
%asm {{
plp
}}
}
sub disable_caseswitch() {
; no-op
}
sub enable_caseswitch() {
; no-op
}
asmsub save_prog8_internals() {
%asm {{
lda P8ZP_SCRATCH_B1
sta save_SCRATCH_ZPB1
lda P8ZP_SCRATCH_REG
sta save_SCRATCH_ZPREG
lda P8ZP_SCRATCH_W1
sta save_SCRATCH_ZPWORD1
lda P8ZP_SCRATCH_W1+1
sta save_SCRATCH_ZPWORD1+1
lda P8ZP_SCRATCH_W2
sta save_SCRATCH_ZPWORD2
lda P8ZP_SCRATCH_W2+1
sta save_SCRATCH_ZPWORD2+1
rts
.section BSS
save_SCRATCH_ZPB1 .byte ?
save_SCRATCH_ZPREG .byte ?
save_SCRATCH_ZPWORD1 .word ?
save_SCRATCH_ZPWORD2 .word ?
.send BSS
; !notreached!
}}
}
asmsub restore_prog8_internals() {
%asm {{
lda save_prog8_internals.save_SCRATCH_ZPB1
sta P8ZP_SCRATCH_B1
lda save_prog8_internals.save_SCRATCH_ZPREG
sta P8ZP_SCRATCH_REG
lda save_prog8_internals.save_SCRATCH_ZPWORD1
sta P8ZP_SCRATCH_W1
lda save_prog8_internals.save_SCRATCH_ZPWORD1+1
sta P8ZP_SCRATCH_W1+1
lda save_prog8_internals.save_SCRATCH_ZPWORD2
sta P8ZP_SCRATCH_W2
lda save_prog8_internals.save_SCRATCH_ZPWORD2+1
sta P8ZP_SCRATCH_W2+1
rts
}}
}
asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register
%asm {{
sta p8_sys_startup.cleanup_at_exit._exitcode
ldx prog8_lib.orig_stackpointer
txs
jmp p8_sys_startup.cleanup_at_exit
}}
}
asmsub exit2(ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y) {
; -- immediately exit the program with result values in the A, X and Y registers.
%asm {{
sta p8_sys_startup.cleanup_at_exit._exitcode
stx p8_sys_startup.cleanup_at_exit._exitcodeX
sty p8_sys_startup.cleanup_at_exit._exitcodeY
ldx prog8_lib.orig_stackpointer
txs
jmp p8_sys_startup.cleanup_at_exit
}}
}
asmsub exit3(ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc) {
; -- immediately exit the program with result values in the A, X and Y registers, and the Carry flag in the status register.
%asm {{
sta p8_sys_startup.cleanup_at_exit._exitcode
lda #0
rol a
sta p8_sys_startup.cleanup_at_exit._exitcarry
stx p8_sys_startup.cleanup_at_exit._exitcodeX
sty p8_sys_startup.cleanup_at_exit._exitcodeY
ldx prog8_lib.orig_stackpointer
txs
jmp p8_sys_startup.cleanup_at_exit
}}
}
inline asmsub progend() -> uword @AY {
%asm {{
lda #<prog8_program_end
ldy #>prog8_program_end
}}
}
inline asmsub progstart() -> uword @AY {
%asm {{
lda #<prog8_program_start
ldy #>prog8_program_start
}}
}
inline asmsub push(ubyte value @A) {
%asm {{
pha
}}
}
inline asmsub pushw(uword value @AY) {
%asm {{
pha
tya
pha
}}
}
inline asmsub push_returnaddress(uword address @XY) {
%asm {{
; push like JSR would: address-1, MSB first then LSB
cpx #0
bne +
dey
+ dex
tya
pha
txa
pha
}}
}
inline asmsub pop() -> ubyte @A {
%asm {{
pla
}}
}
inline asmsub popw() -> uword @AY {
%asm {{
pla
tay
pla
}}
}
}
cx16 {
; the sixteen virtual 16-bit registers in both normal unsigned mode and signed mode (s)
&uword r0 = $0010
&uword r1 = $0012
&uword r2 = $0014
&uword r3 = $0016
&uword r4 = $0018
&uword r5 = $001a
&uword r6 = $001c
&uword r7 = $001e
&uword r8 = $0020
&uword r9 = $0022
&uword r10 = $0024
&uword r11 = $0026
&uword r12 = $0028
&uword r13 = $002a
&uword r14 = $002c
&uword r15 = $002e
; signed word versions
&word r0s = $0010
&word r1s = $0012
&word r2s = $0014
&word r3s = $0016
&word r4s = $0018
&word r5s = $001a
&word r6s = $001c
&word r7s = $001e
&word r8s = $0020
&word r9s = $0022
&word r10s = $0024
&word r11s = $0026
&word r12s = $0028
&word r13s = $002a
&word r14s = $002c
&word r15s = $002e
; ubyte versions (low and high bytes)
&ubyte r0L = $0010
&ubyte r1L = $0012
&ubyte r2L = $0014
&ubyte r3L = $0016
&ubyte r4L = $0018
&ubyte r5L = $001a
&ubyte r6L = $001c
&ubyte r7L = $001e
&ubyte r8L = $0020
&ubyte r9L = $0022
&ubyte r10L = $0024
&ubyte r11L = $0026
&ubyte r12L = $0028
&ubyte r13L = $002a
&ubyte r14L = $002c
&ubyte r15L = $002e
&ubyte r0H = $0011
&ubyte r1H = $0013
&ubyte r2H = $0015
&ubyte r3H = $0017
&ubyte r4H = $0019
&ubyte r5H = $001b
&ubyte r6H = $001d
&ubyte r7H = $001f
&ubyte r8H = $0021
&ubyte r9H = $0023
&ubyte r10H = $0025
&ubyte r11H = $0027
&ubyte r12H = $0029
&ubyte r13H = $002b
&ubyte r14H = $002d
&ubyte r15H = $002f
; signed byte versions (low and high bytes)
&byte r0sL = $0010
&byte r1sL = $0012
&byte r2sL = $0014
&byte r3sL = $0016
&byte r4sL = $0018
&byte r5sL = $001a
&byte r6sL = $001c
&byte r7sL = $001e
&byte r8sL = $0020
&byte r9sL = $0022
&byte r10sL = $0024
&byte r11sL = $0026
&byte r12sL = $0028
&byte r13sL = $002a
&byte r14sL = $002c
&byte r15sL = $002e
&byte r0sH = $0011
&byte r1sH = $0013
&byte r2sH = $0015
&byte r3sH = $0017
&byte r4sH = $0019
&byte r5sH = $001b
&byte r6sH = $001d
&byte r7sH = $001f
&byte r8sH = $0021
&byte r9sH = $0023
&byte r10sH = $0025
&byte r11sH = $0027
&byte r12sH = $0029
&byte r13sH = $002b
&byte r14sH = $002d
&byte r15sH = $002f
; boolean versions
&bool r0bL = $0010
&bool r1bL = $0012
&bool r2bL = $0014
&bool r3bL = $0016
&bool r4bL = $0018
&bool r5bL = $001a
&bool r6bL = $001c
&bool r7bL = $001e
&bool r8bL = $0020
&bool r9bL = $0022
&bool r10bL = $0024
&bool r11bL = $0026
&bool r12bL = $0028
&bool r13bL = $002a
&bool r14bL = $002c
&bool r15bL = $002e
&bool r0bH = $0011
&bool r1bH = $0013
&bool r2bH = $0015
&bool r3bH = $0017
&bool r4bH = $0019
&bool r5bH = $001b
&bool r6bH = $001d
&bool r7bH = $001f
&bool r8bH = $0021
&bool r9bH = $0023
&bool r10bH = $0025
&bool r11bH = $0027
&bool r12bH = $0029
&bool r13bH = $002b
&bool r14bH = $002d
&bool r15bH = $002f
asmsub save_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda cx16.r0,y
sta _cx16_vreg_storage,y
dey
bpl -
rts
.section BSS
_cx16_vreg_storage
.word ?,?,?,?,?,?,?,?
.word ?,?,?,?,?,?,?,?
.send BSS
; !notreached!
}}
}
asmsub restore_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda save_virtual_registers._cx16_vreg_storage,y
sta cx16.r0,y
dey
bpl -
rts
}}
}
sub cpu_is_65816() -> bool {
; Returns true when you have a 65816 cpu, false when it's a 6502.
return false
}
}
p8_sys_startup {
; program startup and shutdown machinery. Needs to reside in normal system ram.
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
%asm {{
sei
cld
clc
; TODO reset screen mode etc etc?
clv
; TODO what about IRQ handler?
cli
rts
}}
}
asmsub init_system_phase2() {
%asm {{
; initialize kernel event interface
lda #<f256.event.packet
sta f256.event.dest+0
stz f256.event.dest+1
;sta f256.args.ext+0 ; check cc65 crt0.s for setup?
;stz f256.args.ext+1
rts
}}
}
asmsub cleanup_at_exit() {
; executed when the main subroutine does rts
%asm {{
lda _exitcarry
lsr a
lda _exitcode
ldx _exitcodeX
ldy _exitcodeY
rts
.section BSS
_exitcarry .byte ?
_exitcode .byte ?
_exitcodeX .byte ?
_exitcodeY .byte ?
.send BSS
; !notreached!
}}
}
}

View File

@ -0,0 +1,446 @@
; Prog8 definitions for the Text I/O and Screen routines for the Foenix F256
;
%import syslib
%import conv
%import shared_cbm_textio_functions
txt {
%option no_symbol_prefixing, ignore_unused
;alias chrout = f256.chrout
asmsub chrout(ubyte character @A) {
%asm {{
jmp f256.chrout
}}
}
; text screen size
const ubyte DEFAULT_WIDTH = f256.DEFAULT_WIDTH
const ubyte DEFAULT_HEIGHT = f256.DEFAULT_HEIGHT
sub clear_screen() {
fill_screen(' ', f256.screen_color)
f256.screen_col = 0
f256.screen_row = 0
}
sub cls() {
clear_screen()
}
sub home() {
f256.screen_row = 0
f256.screen_col = 0
}
sub nl() {
f256.chrout($0d)
;f256.chrout($0a)
}
sub spc() {
f256.chrout(' ')
}
sub bell() {
; beep
; c64.MVOL = 11
; c64.AD1 = %00110111
; c64.SR1 = %00000000
; c64.FREQ1 = 8500
; c64.CR1 = %00010000
; c64.CR1 = %00010001
}
asmsub column(ubyte col @A) clobbers(A, X, Y) {
; ---- set the cursor on the given column (starting with 0) on the current line
%asm {{
sta f256.screen_col
rts
}}
}
asmsub get_column() -> ubyte @Y {
%asm {{
ldy f256.screen_col
rts
}}
}
asmsub row(ubyte rownum @A) clobbers(A, X, Y) {
; ---- set the cursor on the given row (starting with 0) on the current line
%asm {{
sta f256.screen_row
rts
}}
}
asmsub get_row() -> ubyte @X {
%asm {{
ldx f256.screen_row
rts
}}
}
asmsub get_cursor() -> ubyte @X, ubyte @Y {
%asm {{
ldx f256.screen_row
ldy f256.screen_col
rts
}}
}
asmsub fill_screen (ubyte character @ 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)
%asm {{
pha
tya
jsr clear_screencolors
pla
jsr clear_screenchars
rts
}}
}
asmsub clear_screenchars (ubyte character @ A) clobbers(Y) {
; ---- clear the character screen with the given fill character (leaves colors)
; (assumes screen matrix is at the default address)
%asm {{
ldy f256.io_ctrl ; load current mapping
phy ; save to stack
ldy #2
sty f256.io_ctrl ; map in screen memory
ldy #240
- sta f256.Screen+240*0-1,y
sta f256.Screen+240*1-1,y
sta f256.Screen+240*2-1,y
sta f256.Screen+240*3-1,y
sta f256.Screen+240*4-1,y
sta f256.Screen+240*5-1,y
sta f256.Screen+240*6-1,y
sta f256.Screen+240*7-1,y
sta f256.Screen+240*8-1,y
sta f256.Screen+240*9-1,y
sta f256.Screen+240*10-1,y
sta f256.Screen+240*11-1,y
sta f256.Screen+240*12-1,y
sta f256.Screen+240*13-1,y
sta f256.Screen+240*14-1,y
sta f256.Screen+240*15-1,y
sta f256.Screen+240*16-1,y
sta f256.Screen+240*17-1,y
sta f256.Screen+240*18-1,y
sta f256.Screen+240*19-1,y
dey
bne -
ply ; previous mapping from stack
sty f256.io_ctrl ; restore previous map
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)
%asm {{
ldy f256.io_ctrl ; load current mapping
phy ; save to stack
ldy #3
sty f256.io_ctrl ; map in color memory
ldy #240
- sta f256.Colors+240*0-1,y
sta f256.Colors+240*1-1,y
sta f256.Colors+240*2-1,y
sta f256.Colors+240*3-1,y
sta f256.Colors+240*4-1,y
sta f256.Colors+240*5-1,y
sta f256.Colors+240*6-1,y
sta f256.Colors+240*7-1,y
sta f256.Colors+240*8-1,y
sta f256.Colors+240*9-1,y
sta f256.Colors+240*10-1,y
sta f256.Colors+240*11-1,y
sta f256.Colors+240*12-1,y
sta f256.Colors+240*13-1,y
sta f256.Colors+240*14-1,y
sta f256.Colors+240*15-1,y
sta f256.Colors+240*16-1,y
sta f256.Colors+240*17-1,y
sta f256.Colors+240*18-1,y
sta f256.Colors+240*19-1,y
dey
bne -
ply ; previous mapping from stack
sty f256.io_ctrl ; restore previous map
rts
}}
}
sub color (ubyte txtcol) {
f256.screen_color = txtcol
}
sub lowercase() {
; c64.VMCSB |= 2
}
sub uppercase() {
; c64.VMCSB &= ~2
}
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, X, 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
%asm {{
bcc _scroll_screen
+ ; scroll the screen and the color memory
ldx #0
ldy #38
-
.for row=0, row<=24, row+=1
lda f256.Screen + 40*row + 1,x
sta f256.Screen + 40*row + 0,x
lda f256.Colors + 40*row + 1,x
sta f256.Colors + 40*row + 0,x
.next
inx
dey
bpl -
rts
_scroll_screen ; scroll only the screen memory
ldx #0
ldy #38
-
.for row=0, row<=24, row+=1
lda f256.Screen + 40*row + 1,x
sta f256.Screen + 40*row + 0,x
.next
inx
dey
bpl -
rts
}}
}
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A,X) {
; ---- 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
%asm {{
bcc _scroll_screen
+ ; scroll the screen and the color memory
ldx #38
-
.for row=0, row<=24, row+=1
lda f256.Screen + 40*row + 0,x
sta f256.Screen + 40*row + 1,x
lda f256.Colors + 40*row + 0,x
sta f256.Colors + 40*row + 1,x
.next
dex
bpl -
rts
_scroll_screen ; scroll only the screen memory
ldx #38
-
.for row=0, row<=24, row+=1
lda f256.Screen + 40*row + 0,x
sta f256.Screen + 40*row + 1,x
.next
dex
bpl -
rts
}}
}
; stub for call moved to the f256 block.
alias scroll_up = f256.scroll_up
;asmsub scroll_up (bool alsocolors @ Pc) clobbers(A,X) {
; %asm {{
; jmp f256.scroll_up
; }}
;}
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A,X) {
; ---- 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
%asm {{
bcc _scroll_screen
+ ; scroll the screen and the color memory
ldx #39
-
.for row=23, row>=0, row-=1
lda f256.Colors + 40*row,x
sta f256.Colors + 40*(row+1),x
lda f256.Screen + 40*row,x
sta f256.Screen + 40*(row+1),x
.next
dex
bpl -
rts
_scroll_screen ; scroll only the screen memory
ldx #39
-
.for row=23, row>=0, row-=1
lda f256.Screen + 40*row,x
sta f256.Screen + 40*(row+1),x
.next
dex
bpl -
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
%asm {{
pha ; preserve character
jsr f256.chrptr ; calculate offset
pla ; restore character
ldy f256.io_ctrl ; load current mapping
phy ; save on stack
ldy #2
sty f256.io_ctrl ; map in screen memory
ldy #0
sta (f256.screen_ptr), y ; write character
ply
sty f256.io_ctrl ; restore previous mapping
rts
}}
}
asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
; ---- get the character in the screen matrix at the given location
%asm {{
phx ; preserve
tax ; move column to X for call
jsr f256.chrptr ; calculate offset to character
ldy f256.io_ctrl ; load current mapping
phy ; save on stack
ldy #2
sty f256.io_ctrl ; map in screen memory
ldy #0
lda (f256.screen_ptr),y ; get character
ply
sty f256.io_ctrl ; restore previous mapping
plx ; restore
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
%asm {{
pha ; preserve character
jsr f256.chrptr ; calculate offset
pla ; restore character
ldy f256.io_ctrl ; load current mapping
phy ; save on stack
ldy #3
sty f256.io_ctrl ; map in color memory
ldy #0
sta (f256.screen_ptr), y ; write color
ply
sty f256.io_ctrl ; restore previous mapping
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
%asm {{
phx ; preserve
tax ; move column to X for call
jsr f256.chrptr ; calculate offset to character
ldy f256.io_ctrl ; load current mapping
phy ; save on stack
ldy #3
sty f256.io_ctrl ; map in color memory
ldy #0
lda (f256.screen_ptr),y ; get color
ply
sty f256.io_ctrl ; restore previous mapping
plx ; restore
rts
}}
}
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
; ---- set char+color at the given position on the screen
%asm {{
ldx col ; setup parameters
ldy row
jsr f256.chrptr ; calculate offset
ldy f256.io_ctrl ; load current mapping
phy ; save on stack
ldy #2
sty f256.io_ctrl ; map in screen memory
ldy #0
lda character
sta (f256.screen_ptr), y
ldy #3
sty f256.io_ctrl ; map in color memory
ldy #0
lda charcolor
sta (f256.screen_ptr), y
ply ; previous mapping from stack
sty f256.io_ctrl ; restore previous map
rts
}}
}
asmsub plot (ubyte col @ Y, ubyte row @ X) {
%asm {{
sty f256.screen_col
stx f256.screen_row
rts
}}
}
asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns)
%asm {{
lda DEFAULT_WIDTH
rts
}}
}
asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows)
%asm {{
lda DEFAULT_HEIGHT
rts
}}
}
; TODO: jmp to cbm.CHRIN?
asmsub waitkey() -> ubyte @A {
%asm {{
- stz f256.event.type ; invalidate existing event type
jsr f256.event.NextEvent
lda f256.event.type
cmp #f256.event.key.PRESSED
bne -
;lda f256.event.key.raw ; return scan code?
lda f256.event.key.ascii ; return upper or lower ASCII
rts
}}
}
}

View File

@ -1,5 +1,6 @@
Various examples for configurable target machines, such as the NEO6502, Atari 800,
and "tiny" example configurations for the X16 or C64 that provide more example materials.
Foenix F256, and "tiny" example configurations for the X16 or C64 that provide
more example materials.
Look in the Makefile to see how to build or run the various programs.
@ -7,4 +8,4 @@ The user 'adiee5' has been working on a Nintendo Entertainment System (NES) comp
and example program, you can find those efforts here on GitHub: https://github.com/adiee5/prog8-nes-target
Note that the NES is a very alien architecture for Prog8 still and the support is very limited
(for example, prog8 is not aware that the program code usually is going to end up in a ROM cartridge,
and currently still generates code that might nog work in ROM.)
and currently still generates code that might not work in ROM.)

View File

@ -0,0 +1,84 @@
;
; Simplistic example of some text output and
; reading keys.
;
%launcher none
%import textio
main {
str hello = "Hello, World from Prog8!"
sub start() {
ubyte i
ubyte key
txt.cls()
txt.nl()
txt.print(hello)
txt.nl()
for i in 0 to 15 {
txt.color($f0 + i)
txt.print("This is color ")
txt.print_ub(i)
txt.nl()
}
txt.color($f2)
txt.row(30)
txt.print("Using setchr() to draw 'ABC'")
txt.nl()
txt.setchr(20, 20, 'A')
txt.setchr(21, 21, 'B')
txt.setchr(22, 22, 'C')
txt.nl()
txt.print("Using setchr(getchr()) to draw 'ABC'")
txt.nl()
txt.setchr(25, 25, txt.getchr(20, 20))
txt.setchr(26, 26, txt.getchr(21, 21))
txt.setchr(27, 27, txt.getchr(22, 22))
txt.setclr(20,20, $f3)
txt.setclr(21,21, $f4)
txt.setclr(22,22, $f5)
txt.setclr(25,25, txt.getclr(20,20))
txt.setclr(26,26, txt.getclr(21,21))
txt.setclr(27,27, txt.getclr(22,22))
txt.nl()
txt.print("Waiting for a key... ")
key = txt.waitkey()
txt.nl()
txt.print("KEY: ")
txt.chrout(key)
txt.nl()
txt.nl()
txt.print("Press keys to show them, ctrl-c resets machine.")
txt.nl()
; look for keys forever
repeat {
void, key = cbm.GETIN()
if key != $00 {
txt.plot(0,40)
txt.print("key: ")
txt.chrout(key)
txt.spc()
txt.print_ubhex(key, true)
}
; ctrl-c exists / resets machine
if key == $03
break
}
}
}

View File

@ -0,0 +1,55 @@
# configuration file for a Foenix F256 Prog8 compilation target
# generates '.bin' files that are in '.pgz' Foenix format.
cpu = 65C02
encoding = iso
#output_type = LIBRARY
output_type = RAW
#load_address = $0200
#load_address = $1000
load_address = $2000
memtop = $c000
bss_highram_start = 0
bss_highram_end = 0
bss_goldenram_start = 0
bss_goldenram_end = 0
# io_regions specifies memory-mapped I/O registers that should be treated differentely.
# it can be zero or more memory address ranges (inclusive) separated by comma
io_regions = $c000-$dfff
# zeropage scratch variables. zp_scratch_reg must always be zp_scratch_b1+1 !
zp_scratch_b1 = $e2
zp_scratch_reg = $e3
zp_scratch_w1 = $e4
zp_scratch_w2 = $e6
# free zeropage locations for the various zp usage methods
# zero or more zeropage address ranges (inclusive).
#
# $00-$01 - memory & I/O control (MMU)
# $02-$07 - TODO: document (zp_scratch would fit here?)
# $08-$0f - additional (optional) hardware registers (TODO: find & document)
# ...
# $e8-$ef - event array (8 bytes for next event)
# $f0-$ff - kernel arguments table
zp_fullsafe = $22-$e7
zp_kernalsafe = $22-$e7
zp_basicsafe = $22-$e7
# the start of the 32 bytes used by the R0-R15 virtual registers. Can be in Zeropage or elsewhere.
virtual_registers = $10
# Where can we find the standard library (syslib.p8). You can still add more paths manually using -srcdirs
library = ./libraries/f256
# if a non-empty custom launcher code string is supplied, the compiler won't output ANY launcher / init code by itself,
# and instead outputs whatever is specified here. (You can use \n here for newline and \ for line continuantions)
custom_launcher_code =
# additional options passed to the assembler program
#assembler_options = --c256-pgx
assembler_options = --c256-pgz --output-exec=$2000
#assembler_options = --output-exec=\$2000 --c256-pgz
#--c256-pgz --output main.pgz