From 384d9b3fd113c0b555eee5bf2f6e62ea72f49aaf Mon Sep 17 00:00:00 2001 From: hkz Date: Tue, 15 Jul 2025 20:51:44 +0200 Subject: [PATCH] Import empty project --- .gitignore | 5 ++ Makefile | 39 ++++++++++++++ linker-files/linker.scm | 18 +++++++ obj/empty | 0 out/empty | 0 src/line_data.c | 22 ++++++++ src/line_data.h | 9 ++++ src/main.c | 21 ++++++++ src/mem_map.h | 11 ++++ src/mem_registers.h | 49 ++++++++++++++++++ src/monitor_subroutines.c | 19 +++++++ src/monitor_subroutines.h | 85 ++++++++++++++++++++++++++++++ src/tk2k_startup.s | 105 ++++++++++++++++++++++++++++++++++++++ src/utility.c | 87 +++++++++++++++++++++++++++++++ src/utility.h | 26 ++++++++++ 15 files changed, 496 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 linker-files/linker.scm create mode 100644 obj/empty create mode 100644 out/empty create mode 100644 src/line_data.c create mode 100644 src/line_data.h create mode 100644 src/main.c create mode 100644 src/mem_map.h create mode 100644 src/mem_registers.h create mode 100644 src/monitor_subroutines.c create mode 100644 src/monitor_subroutines.h create mode 100644 src/tk2k_startup.s create mode 100644 src/utility.c create mode 100644 src/utility.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f4aa462 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +obj/*.o +obj/*.lst +out/*.bin +out/*.hex +out/*.wav \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6fcf927 --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +VPATH = src + +# Program output +PRG=output + +# Libraries +LIBS=clib-6502.a + + +# Common source files +ASM_SRCS = tk2k_startup.s +C_SRCS = main.c monitor_subroutines.c utility.c + +# Object files +OBJS = $(ASM_SRCS:%.s=%.o) $(C_SRCS:%.c=%.o) + +all: $(PRG).wav + +%.o: %.s + as6502 --core=6502 --list-file=$(@:%.o=obj/%.lst) -o obj/$@ $< + +%.o: %.c + cc6502 --core=6502 -O2 --list-file=$(@:%.o=obj/%.lst) --char-is-unsigned --pedantic-errors -o obj/$@ $< + +$(PRG).hex: $(OBJS) + (cd obj ; ln6502 -g ../linker-files/linker.scm $^ -o ../out/$@ $(LIBS) -l --cross-reference --cstartup=tk2k --no-automatic-placement-rules --output-format intel-hex) + +$(PRG).bin: $(PRG).hex + (cd out ; objcopy -I ihex -O binary $(PRG).hex $(PRG).bin) + +$(PRG).wav: $(PRG).bin + (cd out ; c2t -3 $(PRG).bin,801 $(PRG).wav) + +clean: + -(cd obj ; rm $(OBJS) $(OBJS:%.o=%.lst)) + -rm obj/*.lst + -rm out/*.hex + -rm out/*.bin + -rm out/*.wav \ No newline at end of file diff --git a/linker-files/linker.scm b/linker-files/linker.scm new file mode 100644 index 0000000..2f179cf --- /dev/null +++ b/linker-files/linker.scm @@ -0,0 +1,18 @@ +(define memories + '((memory zeroPage (address (#x72 . #xdf)) (type ram) ;;; Should be starting at 0x56, ending at 0xff, but experiments in the monitor have shown doc is not to be trusted + (section registers zpage zzpage)) + (memory firstPage (address (#x100 . #x1ff)) (section stack)) + (memory reserved (address (#x200 . #x7ff)) (type ram)) + (memory program (address (#x801 . #x1fff)) (type ram) + (section (programStart #x801) (startup #x80e) code)) + (memory displayPage1 (address (#x2000 . #x3fff)) (type ram)) + (memory datamem (address (#x4000 . #x92ff)) (type ram) (section cdata data_init_table)) ;;; usermem goes from 0x4000 to 0x9FFFF (included), we are splitting it, usermem 1 is used as scratch + (memory upperData (address (#x9300 . #x9fff)) (section cstack zdata heap)) + (memory displayPage2 (address (#xa000 . #xbfff)) (type ram)) + (memory io (address (#xc000 . #xc0ff)) (type ram)) + (memory rombank (address (#xc100 . #xffff)) (type rom)) + + (block cstack (size #x800)) + (block stack (size #x100)) + (block heap (size #x400)) + )) \ No newline at end of file diff --git a/obj/empty b/obj/empty new file mode 100644 index 0000000..e69de29 diff --git a/out/empty b/out/empty new file mode 100644 index 0000000..e69de29 diff --git a/src/line_data.c b/src/line_data.c new file mode 100644 index 0000000..992b832 --- /dev/null +++ b/src/line_data.c @@ -0,0 +1,22 @@ +#include + +#include "utility.h" + +const uint16_t line_offset_map[SCREEN_HEIGHT] = { + 0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x0080, 0x0480, 0x0880, 0x0C80, + 0x1080, 0x1480, 0x1880, 0x1C80, 0x0100, 0x0500, 0x0900, 0x0D00, 0x1100, 0x1500, 0x1900, 0x1D00, + 0x0180, 0x0580, 0x0980, 0x0D80, 0x1180, 0x1580, 0x1980, 0x1D80, 0x0200, 0x0600, 0x0A00, 0x0E00, + 0x1200, 0x1600, 0x1A00, 0x1E00, 0x0280, 0x0680, 0x0A80, 0x0E80, 0x1280, 0x1680, 0x1A80, 0x1E80, + 0x0300, 0x0700, 0x0B00, 0x0F00, 0x1300, 0x1700, 0x1B00, 0x1F00, 0x0380, 0x0780, 0x0B80, 0x0F80, + 0x1380, 0x1780, 0x1B80, 0x1F80, 0x0028, 0x0428, 0x0828, 0x0C28, 0x1028, 0x1428, 0x1828, 0x1C28, + 0x00A8, 0x04A8, 0x08A8, 0x0CA8, 0x10A8, 0x14A8, 0x18A8, 0x1CA8, 0x0128, 0x0528, 0x0928, 0x0D28, + 0x1128, 0x1528, 0x1928, 0x1D28, 0x01A8, 0x05A8, 0x09A8, 0x0DA8, 0x11A8, 0x15A8, 0x19A8, 0x1DA8, + 0x0228, 0x0628, 0x0A28, 0x0E28, 0x1228, 0x1628, 0x1A28, 0x1E28, 0x02A8, 0x06A8, 0x0AA8, 0x0EA8, + 0x12A8, 0x16A8, 0x1AA8, 0x1EA8, 0x0328, 0x0728, 0x0B28, 0x0F28, 0x1328, 0x1728, 0x1B28, 0x1F28, + 0x03A8, 0x07A8, 0x0BA8, 0x0FA8, 0x13A8, 0x17A8, 0x1BA8, 0x1FA8, 0x0050, 0x0450, 0x0850, 0x0C50, + 0x1050, 0x1450, 0x1850, 0x1C50, 0x00D0, 0x04D0, 0x08D0, 0x0CD0, 0x10D0, 0x14D0, 0x18D0, 0x1CD0, + 0x0150, 0x0550, 0x0950, 0x0D50, 0x1150, 0x1550, 0x1950, 0x1D50, 0x01D0, 0x05D0, 0x09D0, 0x0DD0, + 0x11D0, 0x15D0, 0x19D0, 0x1DD0, 0x0250, 0x0650, 0x0A50, 0x0E50, 0x1250, 0x1650, 0x1A50, 0x1E50, + 0x02D0, 0x06D0, 0x0AD0, 0x0ED0, 0x12D0, 0x16D0, 0x1AD0, 0x1ED0, 0x0350, 0x0750, 0x0B50, 0x0F50, + 0x1350, 0x1750, 0x1B50, 0x1F50, 0x03D0, 0x07D0, 0x0BD0, 0x0FD0, 0x13D0, 0x17D0, 0x1BD0, 0x1FD0 +}; diff --git a/src/line_data.h b/src/line_data.h new file mode 100644 index 0000000..219744e --- /dev/null +++ b/src/line_data.h @@ -0,0 +1,9 @@ +#ifndef _LINE_DATA_HEADER_ +#define _LINE_DATA_HEADER_ + +#include +#include "utility.h" + +extern const uint16_t line_offset_map[SCREEN_HEIGHT]; + +#endif /* _LINE_DATA_HEADER */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..81535d7 --- /dev/null +++ b/src/main.c @@ -0,0 +1,21 @@ +#include + +#include +#include + +#include "utility.h" +#include "mem_registers.h" +#include "mem_map.h" + +#include "monitor_subroutines.h" + +__task int main () { + POKE(P3_PWRDUP, 0); // Dirty the value checked by the reset vector + PEEK(IO_ROMSEL); // Make sure the ROM is selected + + while(1){ + + }; + + return 0; +} diff --git a/src/mem_map.h b/src/mem_map.h new file mode 100644 index 0000000..1e0fa00 --- /dev/null +++ b/src/mem_map.h @@ -0,0 +1,11 @@ +#ifndef _MEMORY_MAP_HEADER_ +#define _MEMORY_MAP_HEADER_ + +#define DISPLAY_PAGE_SIZE 0x2000 +#define DISPLAY_PAGE_1 0x2000 +#define DISPLAY_PAGE_2 0xA000 + + +#define ROMRAM_BANK 0xC100 + +#endif /* _MEMORY_MAP_HEADER_ */ diff --git a/src/mem_registers.h b/src/mem_registers.h new file mode 100644 index 0000000..63a0394 --- /dev/null +++ b/src/mem_registers.h @@ -0,0 +1,49 @@ +#ifndef _MEM_REGISTERS_HEADER_ +#define _MEM_REGISTERS_HEADER_ + +#include + +#define ROM_MONITOR 0xFF61 + +#define ZP_WNDLFT 0x0020 // 0, left column of scroll window +#define ZP_WNDWDTH 0x0021 // 40, width of scroll window +#define ZP_WNDTOP 0x0022 // 0, top line of the scroll window +#define ZP_WNDBTM 0x0023 // 24, bottom line of the scroll window + +#define ZP_CH 0x0024 // Displacement from window left for the cursor +#define ZP_CV 0x0025 // Displacement from top of screen (now window!) for the cursor + +#define ZP_INVFLAG 0x0032 // Either 0x00 or 0x7F, set text color inversion +#define ZP_PROMPT 0x0033 // Prompt character + +#define ZP_RND 0x004E // Note that this is a 16bit register incremented by the RDKEY func + +#define P3_PWRDUP_REF 0x03F3 +#define P3_PWRDUP 0x03F4 // Already-powered-up indicator. If it is set to the content of 0x03F3 XOR'd with 0xA5, the soft reset vector is considered valid + +typedef struct { + uint8_t kb: 5; // 0:5 + uint8_t prnt: 6; + uint8_t tapein: 7; +} datain; + +#define IO_DATAOUT 0xC000 // (W) To keyboard and printer port +#define IO_DATAIN 0xC010 // (R) Data input from keyboard (0:5), printer (6) and tape (7) +#define IO_TAPEOUT 0xC020 // (R) Data output for tape, read from here to output bit on tape +#define IO_SPEAKER 0xC030 // (R) Speaker toggle +#define IO_DISPLAY_COLOR 0xC050 // (R / W) Access here to enable the colorburst +#define IO_DISPLAY_BW 0xC051 // (R / W) Access here to disable the colorburst +#define IO_MTA_OFF 0xC052 // (?) +#define IO_MTA_ON 0xC053 // (?) +#define IO_DISPLAY_PAGE1 0xC054 // (R / W) Access here to select the primary display page +#define IO_DISPLAY_PAGE2 0xC055 // (R / W) Access here to select the secondary display page +#define IO_MTB_OFF 0xC056 // (?) +#define IO_MTB_ON 0xC057 // (?) +#define IO_PRNT_STRB_LO 0xC058 // (R / W) Access LO/HI/LO or HI/LO/HI consecutively depending on the type of strobe pulse to create +#define IO_PRNT_STRB_HI 0xC059 // (R / W) +#define IO_ROMSEL 0xC05A // (R / W) Access here will make region C100-FFFF a ROM area +#define IO_RAMSEL 0xC05B // (R / W) Access here will make region C100-FFFF a RAM area +#define IO_KB_CTRL_LOW 0xC05E // (R / W) Set the CTRL line to 0, access is through DATAIN +#define IO_KB_CTRL_HI 0xC05F // (R / W) Set the CTRL line to 1 + +#endif /* _MEM_REGISTERS_HEADER_ */ diff --git a/src/monitor_subroutines.c b/src/monitor_subroutines.c new file mode 100644 index 0000000..f1a54cf --- /dev/null +++ b/src/monitor_subroutines.c @@ -0,0 +1,19 @@ +#include "monitor_subroutines.h" + +void sbrt_prntax(uint8_t msb, uint8_t lsb) { + __asm( + " jsr 0xF941\n" + : + : "Ka" (msb), "Kx" (lsb) + : + ); +} + +void sbrt_prbl2(uint8_t count) { + __asm( + " jsr 0xF94A\n" + : + : "Kx" (count) + : + ); +} diff --git a/src/monitor_subroutines.h b/src/monitor_subroutines.h new file mode 100644 index 0000000..03cd408 --- /dev/null +++ b/src/monitor_subroutines.h @@ -0,0 +1,85 @@ +#ifndef _MONITOR_SUBROUTINES_HEADER_ +#define _MONITOR_SUBROUTINES_HEADER_ + +#include + +typedef struct { + uint8_t key; + uint8_t ch; +} rdkey_res; + +inline void sbrt_crout(void); +inline void sbrt_crout1(void); +void sbrt_prntax(uint8_t a, uint8_t x); +inline void sbrt_prblnk(void); +void sbrt_prbl2(uint8_t count); +inline void sbrt_bell(void); +inline rdkey_res sbrt_rdkey(void); + +/*** ***/ + +#define COUT(a) (((void (*)(char))(0xFDED))(a)) +#define COUT1(a) (((void (*)(char))(0xFDF0))(a)) +#define COUTZ(a) (((void (*)(char))(0xFDF6))(a)) +#define PRBYTE(a) (((void (*)(uint8_t))(0xFDDA))(a)) +#define PRHEX(a) (((void (*)(uint8_t))(0xFDE3))(a)) +#define WAIT(a) (((void (*)(uint8_t))(0xFCA8))(a)) +#define BELL1() (((void (*)(uint8_t))(0xFBD9))(0x87)) +#define SETINV() (((void (*)(void))(0xFE80))()) +#define SETNORM() (((void (*)(void))(0xFE84))()) + +inline void sbrt_crout(void) { + __asm volatile( + " jsr 0xFD8E\n" + : + : + : + ); +} + +inline void sbrt_crout1(void) { + __asm volatile( + " jsr 0xFD8B\n" + : + : + : + ); +} + +inline void sbrt_prblnk(void) { + __asm volatile( + " jsr 0xF948\n" + : + : + : "a", "x" + ); +} + +inline void sbrt_bell(void) { + __asm volatile( + " jsr 0xFF3A\n" + : + : + : "a" + ); +} + +inline rdkey_res sbrt_rdkey(void) { + rdkey_res res; + uint8_t key; + uint8_t ch; + + __asm volatile( + " jsr 0xFD0C\n" + : "=Ka"(key), "=Ky"(ch) + : + : "a", "y" + ); + + res.key = key; + res.ch = ch; + + return res; +} + +#endif /* _MONITOR_SUBROUTINES_HEADER_ */ diff --git a/src/tk2k_startup.s b/src/tk2k_startup.s new file mode 100644 index 0000000..146a92a --- /dev/null +++ b/src/tk2k_startup.s @@ -0,0 +1,105 @@ + .rtmodel cstartup,"tk2k" + .rtmodel version, "1" + .rtmodel core, "*" + + ;; External declarations + .section cstack + .section heap + .section data_init_table + .section registers ; pseudo registers in zero page + + .extern main, exit + .extern _Zp, _Vsp, _Vfp + + .pubweak __program_root_section, __program_start + +call: .macro dest + jsr \dest + .endm + + .section programStart, root +__program_root_section: + .word nextLine + .byte 0x0a, 0x00 ; 10 + .byte 0x8c, 0x32, 0x30, 0x36, 0x32, 0x00 ; CALL 2062 +nextLine: .byte 0x00, 0x00 ; end of program + + .section startup, root, noreorder +__program_start: + .section startup, noreorder + jsr __low_level_init + +;;; Initialize data sections if needed. + .section startup, noroot, noreorder + .pubweak __data_initialization_needed + .extern __initialize_sections +__data_initialization_needed: + lda #.byte0 (.sectionStart data_init_table) + sta zp:_Zp + lda #.byte1 (.sectionStart data_init_table) + sta zp:_Zp+1 + lda #.byte0 (.sectionEnd data_init_table) + sta zp:_Zp+2 + lda #.byte1 (.sectionEnd data_init_table) + sta zp:_Zp+3 + call __initialize_sections + + .section startup, noroot, noreorder + .pubweak __call_initialize_global_streams + .extern __initialize_global_streams +__call_initialize_global_streams: + call __initialize_global_streams + +;;; **** Initialize heap if needed. + .section startup, noroot, noreorder + .pubweak __call_heap_initialize + .extern __heap_initialize, __default_heap +__call_heap_initialize: + lda #.byte0 __default_heap + sta zp:_Zp+0 + lda #.byte1 __default_heap + sta zp:_Zp+1 + lda #.byte0 (.sectionStart heap) + sta zp:_Zp+2 + lda #.byte1 (.sectionStart heap) + sta zp:_Zp+3 + lda #.byte0 (.sectionSize heap) + sta zp:_Zp+4 + lda #.byte1 (.sectionSize heap) + sta zp:_Zp+5 + call __heap_initialize + + .section startup, root, noreorder + tsx + stx _InitialStack ; for exit() + lda #0 ; argc = 0 + sta zp:_Zp + sta zp:_Zp+1 + jsr main + jmp exit + +;;; *************************************************************************** +;;; +;;; __low_level_init - custom low level initialization +;;; +;;; This default routine just returns doing nothing. You can provide your own +;;; routine, either in C or assembly for doing custom low leve initialization. +;;; +;;; *************************************************************************** + + .section code + .pubweak __low_level_init +__low_level_init: + rts + +;;; *************************************************************************** +;;; +;;; Keep track of the initial stack pointer so that it can be restores to make +;;; a return back on exit(). +;;; +;;; *************************************************************************** + + .section zdata, bss + .pubweak _InitialStack +_InitialStack: + .space 1 \ No newline at end of file diff --git a/src/utility.c b/src/utility.c new file mode 100644 index 0000000..82c5b8b --- /dev/null +++ b/src/utility.c @@ -0,0 +1,87 @@ +#include "utility.h" + +#include "mem_registers.h" +#include "monitor_subroutines.h" +#include "line_data.h" + +// https://stackoverflow.com/questions/14009765/fastest-way-to-count-bits +uint8_t bit_count(uint8_t b) { + b = (b & 0x55) + (b >> 1 & 0x55); + b = (b & 0x33) + (b >> 2 & 0x33); + b = (b & 0x0f) + (b >> 4 & 0x0f); + + return b; +} + +// Note that the horizontal values here are in group of 7 pixels +void clear_box(uint8_t w, uint8_t h, uint8_t off_x, uint8_t off_y, uint8_t *disp_buf) { + for(uint8_t y = off_y; y < off_y + h; y++) { + uint16_t line_counter = line_offset_map[y]; + + for(uint8_t x = off_x; x < off_x + w; x++) { + disp_buf[line_counter + x] = 0; + } + } +} + +void draw_pic(uint16_t w, uint8_t h, uint8_t off_x, uint8_t off_y, const uint8_t *data, uint8_t *disp_buf) { + uint8_t remainder = 0; + uint8_t remainder_size = 0; + uint8_t pic_width_bytes = w / 8 + ((w % 8) ? 1 : 0); + uint16_t counter; + + for(uint8_t row = 0; row < h; row++) { + uint8_t screen_row = row + off_y; + counter = line_offset_map[screen_row] + (off_x/7); + + remainder = 0; + remainder_size = 0; + + // MSB is the color indicator + for(uint8_t column = 0; column < pic_width_bytes; column++) { + uint8_t pix_data = data[(pic_width_bytes * row) + column]; + uint8_t conv_data = (pix_data << remainder_size) | remainder; + remainder_size++; + remainder = (pix_data >> (8 - remainder_size)) & 0x7F; + disp_buf[counter++] = conv_data & 0x7F; + + if(remainder_size == 7) { + disp_buf[counter++] = remainder & 0x7F; + remainder = 0; + remainder_size = 0; + } + } + + if(remainder_size != 0) { + uint8_t mask = ~((1 << remainder_size) - 1); + disp_buf[counter] = (disp_buf[counter] & mask) | (remainder & 0x7F); + counter++; + } + } +} + + +void print_line(const char* line, uint8_t off_x, uint8_t off_y) { + POKEZ(ZP_CV, off_y); + + uint8_t cur_ch; + for(cur_ch = 0; line[cur_ch]; cur_ch++) { + SETNORM(); + POKEZ(ZP_CH, cur_ch + off_x); + + COUT1(line[cur_ch]|0x80); + + SETINV(); + COUT1(' '|0x80); + + for(uint8_t snd_loop = 0x70; snd_loop; snd_loop--) PEEK(IO_SPEAKER); + + WAIT(0xBE); + } + + SETNORM(); + POKEZ(ZP_CH, cur_ch + off_x); + + COUT1(' '|0x80); + WAIT(0xFF); +} diff --git a/src/utility.h b/src/utility.h new file mode 100644 index 0000000..7983bf4 --- /dev/null +++ b/src/utility.h @@ -0,0 +1,26 @@ +#ifndef _UTILITY_HEADER_ +#define _UTILITY_HEADER_ + +#include + +#define SCREEN_HEIGHT 192 +#define SCREEN_WIDTH_BYTES 128 +#define BYTES_PER_LINE 40 + +#define PEEKZ(a) (*(volatile uint8_t* __attribute__((zpage)))(a)) +#define POKEZ(a, b) ((*(volatile uint8_t* __attribute__((zpage)))(a)) = b) + +#define PEEK(a) (*(volatile uint8_t*)(a)) +#define POKE(a, b) ((*(volatile uint8_t*)(a)) = b) + +#define PEEKW(a) (*(volatile uint16_t*)(a)) +#define POKEW(a, b) ((*(volatile uint16_t*)(a)) = b) + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) + +uint8_t bit_count(uint8_t b); +void clear_box(uint8_t w, uint8_t h, uint8_t off_x, uint8_t off_y, uint8_t *disp_buf); +void draw_pic(uint16_t w, uint8_t h, uint8_t off_x, uint8_t off_y, const uint8_t *data, uint8_t *disp_buf); +void print_line(const char* line, uint8_t off_x, uint8_t off_y); + +#endif /* _UTILITY_HEADER_ */