From c0d6966592a0aaeda220fff1171f057ef14a7ea6 Mon Sep 17 00:00:00 2001 From: Miquel Sabaté Solà Date: Sun, 3 Dec 2023 10:41:39 +0100 Subject: Let's print the result to the screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I have found and vendored a piece of code that allows me to convert a 16-bit number so it can be easily printed on screen. With this, let's be a bit more flashy and print the result to the screen. Signed-off-by: Miquel Sabaté Solà --- assets/alphanum.chr | Bin 0 -> 8192 bytes assets/session.nss | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/1.s | 176 +++++++++++++++++++++++++++++++++++++++++++------ vendor/bcd16.s | 124 +++++++++++++++++++++++++++++++++++ 4 files changed, 467 insertions(+), 19 deletions(-) create mode 100644 assets/alphanum.chr create mode 100644 assets/session.nss create mode 100644 vendor/bcd16.s diff --git a/assets/alphanum.chr b/assets/alphanum.chr new file mode 100644 index 0000000..622eb8e Binary files /dev/null and b/assets/alphanum.chr differ diff --git a/assets/session.nss b/assets/session.nss new file mode 100644 index 0000000..42f6b13 --- /dev/null +++ b/assets/session.nss @@ -0,0 +1,186 @@ +NSTssTXT + +BtnTiles=1 +BtnChecker=0 +BtnSelTiles=0 +BtnChrBank1=0 +BtnChrBank2=1 +BtnGridAll=0 +BtnGridTile=0 +BtnGridAtr=1 +BtnGridBlock=0 +BtnGridScr=0 +BtnPal=1 +BtnTypeIn=0 +BtnFrameAll=1 +BtnFrameSelected=0 +BtnFrameNone=0 +BtnSpriteSnap=0 +BtnSprite8x16=0 +BtnForce8x16=1 +MenuBestOffsets=0 +MenuLossy=0 +MenuThreshold=0 +MenuNoColorData=0 +MenuMetaSprAutoInc=0 +MenuMetaSprSkipZero=0 +MenuMetaSprMerge=0 +MenuSaveIncName=1 +MenuSaveIncAttr=1 +MenuSaveRLE=0 +VarTileViewTagBuf=0 +VarBgPalCur=1 +VarPalActive=0 +VarTileActive=54 +VarBankActive=4096 +VarPPUMask=0 +VarPPUMaskSet0=0 +VarPPUMaskSet1=0 +VarPPUMaskSet2=0 +VarPPUMaskSet3=0 +VarPalBank=0 +VarMetaSpriteActive=0 +VarSpriteActive=0 +VarSpriteGridX=64 +VarSpriteGridY=64 +VarNameW=32 +VarNameH=30 +VarNameViewX=0 +VarNameViewY=-1 +VarNameSelectionL=-1 +VarNameSelectionR=0 +VarNameSelectionT=-1 +VarNameSelectionB=0 +VarNameCopyW=-1 +VarNameCopyH=-1 +VarCHRSelectionL=6 +VarCHRSelectionR=7 +VarCHRSelectionT=3 +VarCHRSelectionB=4 +VarCHRCopyW=-1 +VarCHRCopyH=-1 +VarCHRCopyRect=1 +VarCHRSelectRect=1 +VarCHRBanks=4 +VarCHRBanksUndo=4 +VarCheckpoint_CHRBanks=4 +VarCheckpoint_CHRBanksUndo=4 + + +VarCHRSelected=00[36]0100[c9] + + +Palette=0f3010000f0121310f0616260f0919290f1121310f1222320f1323330f1424340f1525350f1626360f1727370f1828380f1929390f1a2a3a0f1b2b3b0f1c2c3c + + +PalUndo=0f3010000f0121310f0616260f0919290f1121310f1222320f1323330f1424340f1525350f1626360f1727370f1828380f1929390f1a2a3a0f1b2b3b0f1c2c3c + + +CHRMain=00[1101]3c464a52623c00[a]182808[3]3e00[a]3c42023c407e00[a]3c420c02423c00[a]081828487e0800[a]7e407c02423c00[a]3e407c42[2]3c00[a]3e02040810[2]00[a]3c423c42[2]3c00[a]3c42[2]3e023c00[9]3c4299a1[2]99423c00[9]3c42[2]7e42[2]00[a]7c427c42[2]3c00[a]3c4240[2]423c00[a]7c42[4]7c00[a]7c407040[2]7c00[a]3c40[2]7040[2]00[a]3c42404e423c00[a]42[3]7e42[2]00[a]3e08[4]3e00[a]02[3]42[2]3c00[a]44487048444200[a]40[5]7e00[a]42665a42[3]00[a]4262524a464200[a]3c42[4]3c00[a]7c42[2]7c40[2]00[a]3c42[3]3c0600[a]3c42[2]7c444200[a]3e403c02[2]7c00[a]3e08[5]00[a]42[5]3c00[a]22[4]140800[a]42[3]5a664200[a]422418[2]244200[a]41221408[3]00[a]7e040810207e00[f]102000[e]18[2]00[c98] + + +CHRCopy=00[1000] + + +CHRUndo=00[1101]3c464a52623c00[a]182808[3]3e00[a]3c42023c407e00[a]3c420c02423c00[a]081828487e0800[a]7e407c02423c00[a]3e407c42[2]3c00[a]3e02040810[2]00[a]3c423c42[2]3c00[a]3c42[2]3e023c00[9]3c4299a1[2]99423c00[9]3c42[2]7e42[2]00[a]7c427c42[2]3c00[a]3c4240[2]423c00[a]7c42[4]7c00[a]7c407040[2]7c00[a]3c40[2]7040[2]00[a]3c42404e423c00[a]42[3]7e42[2]00[a]3e08[4]3e00[a]02[3]42[2]3c00[a]44487048444200[a]40[5]7e00[a]42665a42[3]00[a]4262524a464200[a]3c42[4]3c00[a]7c42[2]7c40[2]00[a]3c42[3]3c0600[a]3c42[2]7c444200[a]3e403c02[2]7c00[a]3e08[5]00[a]42[5]3c00[a]22[4]140800[a]42[3]5a664200[a]422418[2]244200[a]41221408[3]00[a]7e040810207e00[f]102000[e]18[2]00[c98] + + +NameTable=00[12a]2a261f1b2d1f00311b232e36[3]00[288] + + +NameCopy=00[3c0] + + +NameUndo=00[12a]2a261f1b2d1f00311b232e36[2]00[289] + + +AttrTable=00[40] + + +AttrCopy=00[40] + + +AttrUndo=00[40] + + +MetaSprites=ff[10000] + + +32_TileCollisionProperties=00000000[200] + + +32_TileCollisionPropertiesUndo=00000000[200] + + +PropertyConditions=1[2]0[2]1[2]0[2]1[2]0[2]1[2]0[2]1[2]0[2]1[2]0[2]1[2]0[2]1[2]0[2] + + +PropertyConditionsUndo=1[2]0[2]1[2]0[2]1[2]0[2]1[2]0[2]1[2]0[2]1[2]0[2]1[2]0[2]1[2]0[2] + + +TilePropConditionType=00[7]02 + + +TilePropConditionTypeUndo=00[7]02 + + +Checkpoint_Palette=00[40] + + +Checkpoint_PalUndo=00[40] + + +Checkpoint_CHRMain=00[2000] + + +Checkpoint_CHRUndo=00[2000] + + +Checkpoint_NameTable=00[3c0] + + +Checkpoint_NameUndo=00[3c0] + + +Checkpoint_AttrTable=00[40] + + +Checkpoint_AttrUndo=00[40] + + +Checkpoint_MetaSprites=ff[10000] + + +Checkpoint_32_TileCollisionProperties=00000000[200] + + +Checkpoint_32_TileCollisionPropertiesUndo=00000000[200] + + +Checkpoint_PropertyConditions=[20] + + +Checkpoint_PropertyConditionsUndo=[20] + + +Checkpoint_TilePropConditionType=00[7]02 + + +Checkpoint_TilePropConditionTypeUndo=00[7]02 +propertyCHRlabel0=solid +propertyCHRlabel1=dangerous +propertyCHRlabel2=unlabeled2 +propertyCHRlabel3=unlabeled3 +propertyCHRlabel4=unlabeled4 +propertyCHRlabel5=unlabeled5 +propertyCHRlabel6=escape bit +propertyCHRlabel7=global condition +MetaSpriteBankName=session + +FilterCHR=1 +FilterName=1 +FileNameCHR=C:\users\mssola\Desktop\src\github.com\mssola\aoc2023.nes\assets\alphanum.chr +FileNameName= +FileNamePal= +FileNameMetaSpriteBank= +FileNameTileProps= diff --git a/src/1.s b/src/1.s index 2980951..202c3ee 100644 --- a/src/1.s +++ b/src/1.s @@ -2,13 +2,8 @@ ;; Day 1 https://adventofcode.com/2023/day/1 ;; ;; This program then iterates over the array on `data` and computes the value on -;; each row, then accumulating it into a sum variable. This variable is stored -;; as a 16-bit number on $08-$09. In order to see the actual result, in FCEUX, -;; for example, you need to go to `Tools -> RAM Watch`, and set a watch on -;; address $08 which is unsigned and 2-bytes long. This will show you the end -;; result after a couple of seconds (yes, it's a big ass computation for poor -;; old NES, and it's quite fast considering that we are not rendering anything -;; on screen!). The result must be: `54953`. +;; each row, then accumulating it into a sum variable. The end result will be +;; printed to the screen, which must be `54953`. ;; Luckily for us, good ol' NROM can fit the data set for this exercise :D .segment "HEADER" @@ -24,6 +19,8 @@ .addr nmi, reset, irq .segment "CHARS" + .incbin "../assets/alphanum.chr" + .segment "STARTUP" .segment "CODE" @@ -32,6 +29,7 @@ .include "../include/ppu.s" .include "../include/reset.s" .include "../include/globals.s" +.include "../vendor/bcd16.s" ;; "Variables" used by this program. .scope Vars @@ -80,23 +78,24 @@ ;; The main function will be called at the end of the `reset` vector as defined ;; in `include/reset.s`. .proc main - cli - - lda #%10010000 - sta PPU::CONTROL - lda #%00011110 - sta PPU::MASK - - ;; Initialize variables being used for this program. + ;; Initialize palettes and variables being used for this program. + jsr init_palettes jsr Vars::init ;; This program uses two flags: - ;; - 7 (`render`): whether NMI-code can render stuff on screen. Unused but - ;; reserved out of habit. + ;; - 7 (`render`): whether NMI-code can render stuff on screen. ;; - 6 (`done`): whether the computation has been done. - lda #0 + ;; Note that we are setting the render flag so the initial message is + ;; shown. + lda #%10000000 sta Globals::m_flags + cli + + lda #%10010000 + sta PPU::CONTROL + lda #%00011110 + sta PPU::MASK @loop: jsr compute_next jmp @loop @@ -187,6 +186,17 @@ lda #%01000000 ora Globals::m_flags sta Globals::m_flags + + ;; Now that we are done, convert the 16-bit number into separate bytes so it + ;; can be rendered on NMI-code. + lda Vars::m_sum + 1 + sta bcdNum + lda Vars::m_sum + sta bcdNum + 1 + jsr bcdConvert + + ;; Because of the previous, we can set the `render` flag again. + SET_RENDER_FLAG @end: rts .endproc @@ -221,8 +231,136 @@ rts .endproc -;; Unused. +;; init_palettes copies all the palettes into the proper PPU address. +.proc init_palettes + PPU_ADDR $3F00 + + ldx #0 +@load_palettes_loop: + lda palettes, x + sta PPU::DATA + inx + cpx #$20 + bne @load_palettes_loop + rts +palettes: + ;; Background (only first one used) + .byte $0F, $30, $10, $00 + .byte $0F, $00, $00, $00 + .byte $0F, $00, $00, $00 + .byte $0F, $00, $00, $00 + + ;; Foreground (unused) + .byte $0F, $00, $00, $00 + .byte $0F, $00, $00, $00 + .byte $0F, $00, $00, $00 + .byte $0F, $00, $00, $00 +.endproc + +;; Non-Maskable Interrupts handler. nmi: + bit PPU::STATUS + + ;; Skip rendering if the `render` flag is not set. + bit Globals::m_flags + bpl @nmi_next + + ;; Backup registers. + pha + txa + pha + tya + pha + + ;; If the result is not there yet, then we just print an initial message. + ;; Otherwise print the given result. + bit Globals::m_flags + bvs :+ + jsr print_message + jmp :++ +: + jsr print_result +: + + ;; Reset the scroll. + bit PPU::STATUS + lda #$00 + sta PPU::SCROLL + sta PPU::SCROLL + + ;; And unset the render flag so the `main` code is unblocked. + UNSET_RENDER_FLAG + + ;; Restore registers. + pla + tay + pla + tax + pla +@nmi_next: + rti + +;; Simply print "Please wait..." to the screen. +.proc print_message + WRITE_PPU_DATA $2129, $2A + WRITE_PPU_DATA $212A, $26 + WRITE_PPU_DATA $212B, $1F + WRITE_PPU_DATA $212C, $1B + WRITE_PPU_DATA $212D, $2D + WRITE_PPU_DATA $212E, $1F + + WRITE_PPU_DATA $2130, $31 + WRITE_PPU_DATA $2131, $1B + WRITE_PPU_DATA $2132, $23 + WRITE_PPU_DATA $2133, $2E + WRITE_PPU_DATA $2134, $36 + WRITE_PPU_DATA $2135, $36 + WRITE_PPU_DATA $2136, $36 + + rts +.endproc + +;; Print the result on screen. This assumes that `bcdResult` already contains +;; the proper data. +.proc print_result + ;; Clear tiles that were written by `print_message` and that are not re-used + ;; here. + WRITE_PPU_DATA $2129, $00 + WRITE_PPU_DATA $212A, $00 + WRITE_PPU_DATA $212B, $00 + WRITE_PPU_DATA $212C, $00 + WRITE_PPU_DATA $212D, $00 + WRITE_PPU_DATA $2133, $00 + WRITE_PPU_DATA $2134, $00 + WRITE_PPU_DATA $2135, $00 + WRITE_PPU_DATA $2136, $00 + + ;; And loop so PPU::ADDRESS $2E21-$2E32 has the data as stored on bcdResult, + ;; which is the binary to decimal conversion result when the final + ;; computation was done. + ldx #4 + ldy #$2E +@loop: + ;; PPU address. + bit PPU::STATUS + lda #$21 + sta PPU::ADDRESS + sty PPU::ADDRESS + + ;; PPU data. + lda bcdResult, x + clc + adc #$10 + sta PPU::DATA + dex + iny + cpy #$33 + bne @loop + + rts +.endproc + +;; Unused. irq: rti diff --git a/vendor/bcd16.s b/vendor/bcd16.s new file mode 100644 index 0000000..452b294 --- /dev/null +++ b/vendor/bcd16.s @@ -0,0 +1,124 @@ +; bcd16.s +; version 20060201 +; +; Copyright (C) 2006 Damian Yerrick +; +; Copying and distribution of this file, with or without +; modification, are permitted in any medium without royalty provided +; the copyright notice and this notice are preserved in any source +; code copies. This file is offered as-is, without any warranty. +; +; @mssola: The memory addresses being used here have been modified so they don't +; clash with my programs (from $0x to $8x). + +.p02 + +.exportzp bcdNum, bcdResult +.export bcdConvert + +; bcdConvert +; +; Given a number in bcdNum (16-bit), converts it to 5 decimal digits +; in bcdResult. Unlike most 6502 binary-to-decimal converters, this +; subroutine doesn't use the decimal mode that was removed from the +; 2A03 variant of the 6502 processor. +; +; For each value of n from 4 to 1, it compares the number to 8*10^n, +; then 4*10^n, then 2*10^n, then 1*10^n, each time subtracting if +; possible. After finishing all the comparisons and subtractions in +; each decimal place value, it writes the digit to the output array +; as a byte value in the range [0, 9]. Finally, it writes the +; remainder to element 0. +; +; Extension to 24-bit and larger numbers is straightforward: +; Add a third bcdTable, increase BCD_BITS, and extend the +; trial subtraction. + +; Constants _________________________________________________________ +; BCD_BITS +; The highest possible number of bits in the BCD output. Should +; roughly equal 4 * log10(2) * x, where x is the width in bits +; of the largest binary number to be put in bcdNum. +; bcdTableLo[y], bcdTableHi[y] +; Contains (1 << y) converted from BCD to binary. +BCD_BITS = 19 + +; Variables _________________________________________________________ +; bcdNum (input) +; Number to be converted to decimal (16-bit little endian). +; Overwritten. +; bcdResult (output) +; Decimal digits of result (5-digit little endian). +; X +; Offset of current digit being worked on. +; Y +; Offset into bcdTable*. +; curDigit +; The lower holds the digit being constructed. +; The upper nibble contains a sentinel value; when a 1 is shifted +; out, the byte is complete and should be copied to result. +; (This behavior is called a "ring counter".) +; Overwritten. +; b +; Low byte of the result of trial subtraction. +; Overwritten. +bcdNum = $80 +bcdResult = $82 +curDigit = $87 +b = $82 + +; +; Completes within 670 cycles. +; + +bcdConvert: + lda #$80 >> ((BCD_BITS - 1) & 3) + sta curDigit + ldx #(BCD_BITS - 1) >> 2 + ldy #BCD_BITS - 5 + +@loop: + ; Trial subtract this bit to A:b + sec + lda bcdNum + sbc bcdTableLo,y + sta b + lda bcdNum+1 + sbc bcdTableHi,y + + ; If A:b > bcdNum then bcdNum = A:b + bcc @trial_lower + sta bcdNum+1 + lda b + sta bcdNum +@trial_lower: + + ; Copy bit from carry into digit and pick up + ; end-of-digit sentinel into carry + rol curDigit + dey + bcc @loop + + ; Copy digit into result + lda curDigit + sta bcdResult,x + lda #$10 ; Empty digit; sentinel at 4 bits + sta curDigit + ; If there are digits left, do those + dex + bne @loop + lda bcdNum + sta bcdResult + rts + +bcdTableLo: + .byt <10, <20, <40, <80 + .byt <100, <200, <400, <800 + .byt <1000, <2000, <4000, <8000 + .byt <10000, <20000, <40000 + +bcdTableHi: + .byt >10, >20, >40, >80 + .byt >100, >200, >400, >800 + .byt >1000, >2000, >4000, >8000 + .byt >10000, >20000, >40000 -- cgit v1.2.3