Adjusted the PCE's document, the start-up code, and the PCE library test makefile. That makefile shows how to post-process the linker's output file.
-# linker config to produce simple NEC PC-Engine cartridge (.pce)
-
+# linker config. to produce a NEC PC-Engine 8K, 16K, or 32K image (.bin)
SYMBOLS {
+ __CARTSIZE__: type = weak, value = $2000; # $2000, $4000, or $8000
__STACKSIZE__: type = weak, value = $0300; # 3 pages stack
}
-
MEMORY {
- # FIXME: is this correct? the first 3? bytes cant be used?
- ZP: file = "", start = $0003, size = $00FD, type = rw, define = yes;
-
- # reset-bank and hardware vectors
- ROM0: file = %O, start = $E000, size = $1FF6, fill = yes, define = yes;
- ROMV: file = %O, start = $FFF6, size = $000A, fill = yes;
-
- # first RAM page (also contains stack and zeropage)
- RAM: file = "", start = $2200, size = $1e00, define = yes;
+ ZP: file = "", start = $0000, size = $0100, define = yes;
+ # RAM bank
+ MAIN: file = "", start = $2200, size = $1E00 - __STACKSIZE__, define = yes;
+ # ROM banks, before swapping, and after mapping
+ ROM: file = %O, start = $10000 - __CARTSIZE__, size = __CARTSIZE__, fill = yes, fillval = $FF;
}
-
SEGMENTS {
- ZEROPAGE: load = ZP, type = zp, define = yes;
- EXTZP: load = ZP, type = zp, define = yes, optional = yes;
- APPZP: load = ZP, type = zp, define = yes, optional = yes;
- STARTUP: load = ROM0, type = ro, define = yes;
- ONCE: load = ROM0, type = ro, optional = yes;
- CODE: load = ROM0, type = ro, define = yes;
- RODATA: load = ROM0, type = ro, define = yes;
- DATA: load = ROM0, run = RAM, type = rw, define = yes;
- BSS: load = RAM, type = bss, define = yes;
- VECTORS: load = ROMV, type = rw, define = yes;
+ ZEROPAGE: load = ZP, type = zp;
+ EXTZP: load = ZP, type = zp, optional = yes;
+ APPZP: load = ZP, type = zp, optional = yes;
+ DATA: load = ROM, run = MAIN, type = rw, define = yes;
+ INIT: load = MAIN, type = bss, optional = yes;
+ BSS: load = MAIN, type = bss, define = yes;
+ RODATA: load = ROM, type = ro;
+ CODE: load = ROM, type = ro;
+ LOWCODE: load = ROM, type = ro, optional = yes;
+ ONCE: load = ROM, type = ro, optional = yes;
+ STARTUP: load = ROM, type = ro, start = $FFF6 - $0066;
+ VECTORS: load = ROM, type = ro, start = $FFF6;
}
-
FEATURES {
CONDES: type = constructor,
label = __CONSTRUCTOR_TABLE__,
<article>
<title>PC-Engine (TurboGrafx) System specific information for cc65
-<author>
-<url url="mailto:groepaz@gmx.net" name="Groepaz/Hitmen">
-<date>2016-09-29
+<author><url url="mailto:groepaz@gmx.net" name="Groepaz/Hitmen">,<newline>
+<url url="mailto:greg.king5@verizon.net" name="Greg King">
+<date>2018-02-12
<abstract>
An overview over the PCE runtime system as it is implemented for the
information.
+
<sect>Binary format<p>
-The standard binary output format generated by the linker for the PCE target
-is a cartridge image with no header. It is of course possible to change this
-behaviour by using a modified startup file and linker config.
+The binary output file generated by the linker, for the PCE target, is an
+image, with no header, that has 8K bytes in the wrong place. That file must be
+post-processed; the 8K at the end must be moved to the front of the image.
+
+On POSIX systems, the <tt/dd/ command and the shell give a convenient way to do
+it. Here is an example of their use:
+<tscreen><verb>
+dd if=conio.bin bs=8K skip=3 > conio.pce
+dd if=conio.bin bs=8K count=3 >> conio.pce
+</verb></tscreen>
+The first command grabs the last 8K of a 32K file, and writes it as the first
+part of a new file. The second command reads all but the last part of the old
+file, and appends it to the new file.
+<tscreen><verb>
++--------+--------+--------+--------+
+| Bank 1 | Bank 2 | Bank 3 | Bank 0 | <-- "conio.bin"
++--------+--------+--------+--------+
+
++--------+--------+--------+--------+
+| Bank 0 | Bank 1 | Bank 2 | Bank 3 | <-- "conio.pce"
++--------+--------+--------+--------+
+</verb></tscreen>
+<em/Note/: That <tt/.pce/ file shows the format of the ROM cartridge that is
+plugged into a PC-Engine. But, that <tt/.bin/ file shows what programs
+actually see when they execute the code in that cartridge.
+
+
<sect>Memory layout<p>
<tag/Stack/
The C runtime stack is located in system RAM at $3FFF and growing downwards.
- <tag/BSS and Data/
-
- The BSS (uninitialized variables) and Data (initialized variables) sections are
- placed one after the other into system RAM at $2000.
+ <tag/Data and BSS/
+ The Data (initialized variables) and BSS (uninitialized variables) sections are
+ placed one after the other into system RAM at $2200.
<tag/Heap/
- The C heap is located after the end of the Data section and grows towards the C
+ The C heap is located after the end of the BSS section; and, grows towards the C
runtime stack.
<tag/Code/
- The startup code is located at $E000 in the System/Hardware bank. Further
- code can be placed in other ROM banks, this must be done manually however.
+ In an 8K ROM cartridge, code and read-only data are located between
+ $E000 and $FFF5 in the System bank.
+ In a 16K cartridge, code and read-only data are located between $C000
+ and $FFF5.
+
+ In a 32K cartridge, code and read-only data are located between $8000
+ and $FFF5.
</descrip><p>
<sect>Other hints<p>
<itemize>
-<item>a good emulator to use for PC-Engine is "mednafen" (<url url="http://mednafen.fobby.net/">)
+<item><url url="https://mednafen.github.io/" name= "Mednafen"> is a good
+emulator to use for the PC-Engine.
</itemize>
some useful resources on PCE coding:
</enum>
</article>
-
-
-
; by Groepaz/Hitmen <groepaz@gmx.net>
; based on code by Ullrich von Bassewitz <uz@cc65.org>
;
-; This must be the *first* file on the linker command line
+; 2018-02-11, Greg King
;
.export _exit
.export __STARTUP__ : absolute = 1 ; Mark as startup
.import initlib, donelib
- .import push0, _main, zerobss
- .import initheap
+ .import push0, _main
.import IRQStub
- ; Linker generated
- .import __RAM_START__, __RAM_SIZE__
- .import __ROM0_START__, __ROM0_SIZE__
- .import __ROM_START__, __ROM_SIZE__
- .import __STARTUP_LOAD__,__STARTUP_RUN__, __STARTUP_SIZE__
- .import __CODE_LOAD__,__CODE_RUN__, __CODE_SIZE__
- .import __RODATA_LOAD__,__RODATA_RUN__, __RODATA_SIZE__
- .import __DATA_LOAD__,__DATA_RUN__, __DATA_SIZE__
- .import __BSS_SIZE__
+ ; Linker-generated
+ .import __CARTSIZE__
+ .import __DATA_LOAD__, __DATA_RUN__, __DATA_SIZE__
+ .import __BSS_RUN__, __BSS_SIZE__
+ .import __MAIN_START__, __MAIN_SIZE__, __STACKSIZE__
.include "pce.inc"
.include "extzp.inc"
.importzp sp
- .importzp ptr1,ptr2
- .importzp tmp1,tmp2,tmp3
; ------------------------------------------------------------------------
; Place the startup code in a special segment.
ldx #$FF ; Stack top ($21FF)
txs
- ; At startup all MPRs are set to 0, so init them
- lda #$ff
- tam #%00000001 ; 0000-1FFF = Hardware page
+ ; At power-on, most MPRs have random values; so, initiate them.
+ lda #$FF
+ tam #%00000001 ; $0000-$1FFF = Hardware bank
lda #$F8
- tam #%00000010 ; 2000-3FFF = Work RAM
-
- ; FIXME: setup a larger block of memory to use with C-code
+ tam #%00000010 ; $2000-$3FFF = Work RAM
;lda #$F7
- ;tam #%00000100 ; 4000-5FFF = Save RAM
- ;lda #1
- ;tam #%00001000 ; 6000-7FFF Page 2
- ;lda #2
- ;tam #%00010000 ; 8000-9FFF Page 3
- ;lda #3
- ;tam #%00100000 ; A000-BFFF Page 4
+ ;tam #%00000100 ; $4000-$47FF = 2K Battery-backed RAM
;lda #4
- ;tam #%01000000 ; C000-DFFF Page 5
- ;lda #0
- ;tam #%10000000 ; e000-fFFF hucard/syscard bank 0
-
- ; Clear work RAM (2000-3FFF)
- stz <$00
- tii $2000, $2001, $1FFF
+ ;tam #%00001000 ; $6000-$7FFF
+
+ lda #$01
+ ldx #>$8000
+ cpx #>__CARTSIZE__
+ bcc @L1 ;(blt)
+ tam #%00010000 ; $8000-$9FFF = ROM bank 1 (32K block of ROM)
+ inc a
+ tam #%00100000 ; $A000-$BFFF = ROM bank 2
+ inc a
+@L1: tam #%01000000 ; $C000-$DFFF = ROM bank 3 (32K) or 1 (16K)
+ ;lda #$00 ; (The reset default)
+ ;tam #%10000000 ; $E000-$FFFF hucard/syscard bank 0
; Initialize hardware
stz TIMER_CTRL ; Timer off
lda #$05
sta IRQ_MASK ; IRQ1=on
- ; Clear the BSS data
- jsr zerobss
-
; Copy the .data segment to RAM
tii __DATA_LOAD__, __DATA_RUN__, __DATA_SIZE__
+ ; Clear the .bss segment
+ stz __BSS_RUN__
+ tii __BSS_RUN__, __BSS_RUN__ + 1, __BSS_SIZE__ - 1
+
; Set up the stack
- lda #<(__RAM_START__+__RAM_SIZE__)
- ldx #>(__RAM_START__+__RAM_SIZE__)
+ lda #<(__MAIN_START__ + __MAIN_SIZE__ + __STACKSIZE__)
+ ldx #>(__MAIN_START__ + __MAIN_SIZE__ + __STACKSIZE__)
sta sp
stx sp + 1
.PHONY: all clean test
+# Size of cartridge to generate.
+# Possible values:
+# 8K = 0x2000
+# 16K = 0x4000
+# 32K = 0x8000
+CARTSIZE := 0x2000
+
+ifeq (${CARTSIZE},0x8000)
+COUNT := 3
+else
+COUNT := 1
+endif
+
all: conio.pce
-conio.pce: conio.c
- ../../../bin/cl65 -t pce conio.c --mapfile conio.map -o conio.pce
+%.pce: %.bin
+ dd if=$< bs=8K skip=${COUNT} > $@
+ dd if=$< bs=8K count=${COUNT} >> $@
+
+%.bin: %.c ../../../lib/pce.lib
+ ../../../bin/cl65 -t pce $< -Wl -D__CARTSIZE__=${CARTSIZE} -m $*.map -o $@
clean:
- $(RM) conio.o conio.pce conio.map
+ $(RM) conio.o conio.???
test: conio.pce
- mednafen -force_module pce conio.pce
+ mednafen -force_module pce $<
p[8],p[9],p[10],p[11],p[12],p[13],p[14],p[15]
);
}
- memcpy(p, main, 0); /* test that a zero length doesn't copy 64K */
+ memcpy(p, main, i = 0); /* test that a zero length doesn't copy 64K */
gotoxy(0,ysize - 1);
for (i = 0; i < xsize; ++i) {