From 1f02f81b3de1eb1330632294f6f7aa447c9b9202 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Wed, 8 Jul 2020 10:29:49 +0200 Subject: [PATCH] Working on lazynes print.c demonstrating lnList() --- src/main/kc/include/nes.h | 8 +- src/main/kc/lib/nes.c | 4 +- src/test/kc/complex/lazynes/lazynes.c | 86 ++++++++++++++++++---- src/test/kc/complex/lazynes/lazynes.h | 37 +++++----- src/test/kc/complex/lazynes/print.c | 49 ++++++++++++ src/test/vs.code/kickc-test.code-workspace | 3 +- 6 files changed, 152 insertions(+), 35 deletions(-) create mode 100644 src/test/kc/complex/lazynes/print.c diff --git a/src/main/kc/include/nes.h b/src/main/kc/include/nes.h index a6cdea93e..52ce861b8 100644 --- a/src/main/kc/include/nes.h +++ b/src/main/kc/include/nes.h @@ -96,10 +96,16 @@ char ppuDataRead(); void ppuDataFill(void* const ppuData, char val, unsigned int size); // Transfer a number of bytes from the CPU memory to the PPU memory +// - ppuData : Pointer in the PPU memory +// - cpuData : Pointer to the CPU memory (RAM of ROM) +// - size : The number of bytes to transfer +void ppuDataTransfer(void* const ppuData, void* const cpuData, unsigned int size); + +// Transfer a number of bytes from the PPU memory to the CPU memory // - cpuData : Pointer to the CPU memory (RAM of ROM) // - ppuData : Pointer in the PPU memory // - size : The number of bytes to transfer -void ppuDataTransfer(void* const ppuData, void* const cpuData, unsigned int size); +void ppuDataFetch(void* const cpuData, void* const ppuData, unsigned int size); // Transfer a 2x2 tile into the PPU memory // - ppuData : Pointer in the PPU memory diff --git a/src/main/kc/lib/nes.c b/src/main/kc/lib/nes.c index 27e572be0..d514f6398 100644 --- a/src/main/kc/lib/nes.c +++ b/src/main/kc/lib/nes.c @@ -142,8 +142,8 @@ void ppuDataFill(void* const ppuData, char val, unsigned int size) { } // Transfer a number of bytes from the CPU memory to the PPU memory -// - cpuData : Pointer to the CPU memory (RAM of ROM) // - ppuData : Pointer in the PPU memory +// - cpuData : Pointer to the CPU memory (RAM of ROM) // - size : The number of bytes to transfer void ppuDataTransfer(void* const ppuData, void* const cpuData, unsigned int size) { ppuDataPrepare(ppuData); @@ -154,8 +154,8 @@ void ppuDataTransfer(void* const ppuData, void* const cpuData, unsigned int size } // Transfer a number of bytes from the PPU memory to the CPU memory -// - ppuData : Pointer in the PPU memory // - cpuData : Pointer to the CPU memory (RAM of ROM) +// - ppuData : Pointer in the PPU memory // - size : The number of bytes to transfer void ppuDataFetch(void* const cpuData, void* const ppuData, unsigned int size) { ppuDataPrepare(ppuData); diff --git a/src/test/kc/complex/lazynes/lazynes.c b/src/test/kc/complex/lazynes/lazynes.c index babb5b573..7b8dddbde 100644 --- a/src/test/kc/complex/lazynes/lazynes.c +++ b/src/test/kc/complex/lazynes/lazynes.c @@ -76,6 +76,20 @@ volatile char scroll_x; // Scroll y-position volatile char scroll_y; +// Update list with data to be moved to VRAM during blank +// The data is moved by when lnSync() is called +// - The format of the update list is an array of unsigned bytes. +// - There can be 3 different commands in the update list: +// a) addressHi, addressLo, value +// b) addressHi|lfHor, addressLo, amountOfBytes, byte1, byte2, byte3, ... +// c) addressHi|lfVer, addressLo, amountOfBytes, byte1, byte2, byte3, ... +// - Multiple commands can be queued in one list, +// but there can only be one activated updatelist at a time. +// - The end of the list is marked by lfEnd! (important!) +// - It's the same format that's used in set_vram_update() of Shiru's neslib +// See https://nesdoug.com/2017/04/13/my-neslib-notes/ +char * volatile vram_update_list; + // NMI Called when the PPU refreshes the screen (also known as the V-Blank period) interrupt(hardware_stack) void vblank() { // DMA transfer the entire sprite buffer to the PPU @@ -130,7 +144,11 @@ ubyte lnSync(ubyte flags) { // Update the mode sync_mode = flags; + // Move any pending data to the VRAM + lnListTransfer(); + // TODO: Handle lfSplit = 2 : activates split mode, NMI waits for SPR0HIT and sets registers + // Return number of vblank since last sync char res = vblank_count; vblank_count = 0; @@ -147,23 +165,65 @@ void lnPush(uword o, ubyte a, void* s) { ppuDataTransfer(o, s, a); } - // Write data into nametables, palettes, CHRRAM, etc. + // Plan a write of data into nametables, palettes, CHRRAM, etc. + // The data will be written next time lnSync() is called. // (Screen has to be visible, doesn't work in blank mode!) // updateList: Pointer to update list // -// TODO: void lnList(void* updateList); -// TODO: enum { lfHor=64, lfVer=128, lfEnd=255 }; +// remarks: +// - The format of the update list is an array of unsigned bytes. +// - There can be 3 different commands in the update list: +// a) addressHi, addressLo, value +// b) addressHi|lfHor, addressLo, amountOfBytes, byte1, byte2, byte3, ... +// c) addressHi|lfVer, addressLo, amountOfBytes, byte1, byte2, byte3, ... +// - Multiple commands can be queued in one list, +// but there can only be one activated updatelist at a time. +// - The end of the list is marked by lfEnd! (important!) +// - It's the same format that's used in set_vram_update() of Shiru's neslib +// See https://nesdoug.com/2017/04/13/my-neslib-notes/ +void lnList(void* update_list) { + vram_update_list = update_list; +} + +// Execute any planned transfer of data into nametables, palettes, CHRRAM, etc. by lnList() +void lnListTransfer() { + if(vram_update_list) { + // Index into the update list (assumes no more than 256 bytes) + char idx = 0; + for(;;) { + char addrHi = vram_update_list[idx++]; + // Have we reached the end of the lsit + if(addrHi==0xff) break; + if(addrHi&lfHor) { + // The write is horizontal + char addrLo = vram_update_list[idx++]; + char* ppuAddr = (char*)(uword){ addrHi&0x3f, addrLo }; + char size = vram_update_list[idx++]; + ppuDataTransfer(ppuAddr, vram_update_list+idx, size); + } else if(addrHi&lfVer) { + // The write is vertical + char addrLo = vram_update_list[idx++]; + char* ppuAddr = (char*)(uword){ addrHi&0x3f, addrLo }; + char size = vram_update_list[idx++]; + // Set vertical mode ibit in PPUCTRL + char ppuCtrl = PPU->PPUCTRL; + PPU->PPUCTRL = ppuCtrl|4; + ppuDataTransfer(ppuAddr, vram_update_list+idx, size); + // restore PPUCTRL + PPU->PPUCTRL = ppuCtrl; + } else { + // The write is single-byte + char addrLo = vram_update_list[idx++]; + char* ppuAddr = (char*)(uword){ addrHi, addrLo }; + char value = vram_update_list[idx++]; + ppuDataSet(ppuAddr, value); + } + } + // Set update list to zero + vram_update_list = 0; + } +} - // remarks: - // - The format of the update list is an array of unsigned bytes. - // - There can be 3 different commands in the update list: - // a) addressHi, addressLo, value - // b) addressHi|lfHor, addressLo, amountOfBytes, byte1, byte2, byte3, ... - // c) addressHi|lfVer, addressLo, amountOfBytes, byte1, byte2, byte3, ... - // - Multiple commands can be queued in one list, - // but there can only be one activated updatelist at a time. - // - The end of the list is marked by lfEnd! (important!) - // - It's the same format that's used in set_vram_update() of Shiru's neslib // Scroll background diff --git a/src/test/kc/complex/lazynes/lazynes.h b/src/test/kc/complex/lazynes/lazynes.h index af4cbc3a5..7ec7242e9 100644 --- a/src/test/kc/complex/lazynes/lazynes.h +++ b/src/test/kc/complex/lazynes/lazynes.h @@ -39,27 +39,28 @@ void lnPush(uword o, ubyte a, void* s); // (Screen has to be visible, doesn't work in blank mode!) // updateList: Pointer to update list // -// TODO: void lnList(void* updateList); +// remarks: +// - The format of the update list is an array of unsigned bytes. +// - There can be 3 different commands in the update list: +// a) addressHi, addressLo, value +// b) addressHi|lfHor, addressLo, amountOfBytes, byte1, byte2, byte3, ... +// c) addressHi|lfVer, addressLo, amountOfBytes, byte1, byte2, byte3, ... +// - Multiple commands can be queued in one list, +// but there can only be one activated updatelist at a time. +// - The end of the list is marked by lfEnd! (important!) +// - It's the same format that's used in set_vram_update() of Shiru's neslib +// See https://nesdoug.com/2017/04/13/my-neslib-notes/ +void lnList(void* update_list); + +// Constants used to control VRAM updates in the list passed to lnList() enum { lfHor=64, lfVer=128, lfEnd=255 }; - // - // remarks: - // - The format of the update list is an array of unsigned bytes. - // - There can be 3 different commands in the update list: - // a) addressHi, addressLo, value - // b) addressHi|lfHor, addressLo, amountOfBytes, byte1, byte2, byte3, ... - // c) addressHi|lfVer, addressLo, amountOfBytes, byte1, byte2, byte3, ... - // - Multiple commands can be queued in one list, - // but there can only be one activated updatelist at a time. - // - The end of the list is marked by lfEnd! (important!) - // - It's the same format that's used in set_vram_update() of Shiru's neslib - - // Common offsets for lnPush() and lnList() - const uword lnNameTab0=0x2000, lnNameTab1=0x2400, lnNameTab2=0x2800, lnNameTab3=0x2C00, +// Common offsets for lnPush() and lnList() +const uword lnNameTab0=0x2000, lnNameTab1=0x2400, lnNameTab2=0x2800, lnNameTab3=0x2C00, lnAttrTab0=0x23C0, lnAttrTab1=0x27C0, lnAttrTab2=0x2BC0, lnAttrTab3=0x2FC0, - lnBackCol=0x3F00, - lnChrPal0=0x3F01, lnChrPal1=0x3F05, lnChrPal2=0x3F09, lnChrPal3=0x3F0D, - lnSprPal0=0x3F11, lnSprPal1=0x3F15, lnSprPal2=0x3F19, lnSprPal3=0x3F1D; + lnBackCol=0x3F00, + lnChrPal0=0x3F01, lnChrPal1=0x3F05, lnChrPal2=0x3F09, lnChrPal3=0x3F0D, + lnSprPal0=0x3F11, lnSprPal1=0x3F15, lnSprPal2=0x3F19, lnSprPal3=0x3F1D; // Scroll background // x: New horizotnal scrolling offset in pixels, allowed range: [0..511] diff --git a/src/test/kc/complex/lazynes/print.c b/src/test/kc/complex/lazynes/print.c new file mode 100644 index 000000000..b1c72656d --- /dev/null +++ b/src/test/kc/complex/lazynes/print.c @@ -0,0 +1,49 @@ +// lazyNES print demo (using lnlist) + // lazyNES - As lazy as possible NES hardware support library for vbcc6502 + // (happily cooperates with Shiru's famitone2 replay code) +// V1.0, 'Lazycow 2020 + +// Ported to KickC 2020 by Jesper Gravgaard +// Original Source VBCC alpha 2 http://www.ibaug.de/vbcc/vbcc6502_2.zip + +#pragma target(nes) +#include "lazynes.h" + +// A string in ROM +#pragma data_seg(Data) +ubyte b_init[]={0,0,10,'B','U','B','B','L','E','S',':',0,0,lfEnd}; + +// A string in RAM +#pragma data_seg(GameRam) +ubyte b[14]; + + // print some text in the static area using lnList +void Print(uword offset, ubyte value) { + b[0]=(ubyte)(offset>>8)|lfHor; b[1]=(ubyte)offset; b[11]=b[12]='0'; + while (value>=10) { ++b[11]; value-=10; } + while (value>=1) { ++b[12]; value-=1; } + lnList(b); +} + +int lnMain() { + // Copy string from ROM to RAM + for(char i=0;i8) { + tics=0; + objects = (objects+1)&0x3f; + } + // display a number + Print(lnNameTab0+32,objects+1); + lnSync(0); // sync with vblank + } + return 0; +} diff --git a/src/test/vs.code/kickc-test.code-workspace b/src/test/vs.code/kickc-test.code-workspace index 87901cdd9..4849302c3 100644 --- a/src/test/vs.code/kickc-test.code-workspace +++ b/src/test/vs.code/kickc-test.code-workspace @@ -11,7 +11,8 @@ "files.associations": { "print.h": "c", "printf.h": "c", - "c64.h": "c" + "c64.h": "c", + "sstream": "c" }, "kickassembler.assemblerJar": "/Applications/KickAssembler/KickAss.jar" }