From b2615b1a9847e7ea90b3ff01603a143af8679338 Mon Sep 17 00:00:00 2001 From: Joonas Javanainen Date: Sun, 7 Jan 2024 20:04:35 +0200 Subject: [PATCH] Migrate to typst (#12) --- .github/workflows/ci.yml | 55 +- .gitignore | 7 +- .latexmkrc | 2 - CMakeLists.txt | 78 - appendix/external-bus.tex | 227 --- appendix/external-bus.typ | 227 +++ appendix/memory-map.tex | 331 ----- appendix/memory-map.typ | 199 +++ appendix/opcode-tables.tex | 134 -- appendix/opcode-tables.typ | 98 ++ appendix/pinouts.tex | 43 - appendix/pinouts.typ | 34 + chapter/cartridges/huc1.tex | 14 - chapter/cartridges/huc1.typ | 8 + chapter/cartridges/huc3.tex | 14 - chapter/cartridges/huc3.typ | 9 + chapter/cartridges/mbc1.tex | 455 ------ chapter/cartridges/mbc1.typ | 331 +++++ chapter/cartridges/mbc2.tex | 186 --- chapter/cartridges/mbc2.typ | 133 ++ chapter/cartridges/mbc3.tex | 15 - chapter/cartridges/mbc3.typ | 5 + chapter/cartridges/mbc30.tex | 23 - chapter/cartridges/mbc30.typ | 9 + chapter/cartridges/mbc5.tex | 134 -- chapter/cartridges/mbc5.typ | 93 ++ chapter/cartridges/mbc6.tex | 13 - chapter/cartridges/mbc6.typ | 5 + chapter/cartridges/mbc7.tex | 11 - chapter/cartridges/mbc7.typ | 5 + chapter/cartridges/mmm01.tex | 11 - chapter/cartridges/mmm01.typ | 5 + chapter/cartridges/tama5.tex | 11 - chapter/cartridges/tama5.typ | 5 + chapter/cpu/instruction-set.tex | 2351 ------------------------------ chapter/cpu/instruction-set.typ | 1854 +++++++++++++++++++++++ chapter/cpu/timing.tex | 84 -- chapter/cpu/timing.typ | 123 ++ chapter/peripherals/boot-rom.tex | 122 -- chapter/peripherals/boot-rom.typ | 80 + chapter/peripherals/dma.tex | 112 -- chapter/peripherals/dma.typ | 64 + chapter/peripherals/p1.tex | 42 - chapter/peripherals/p1.typ | 25 + chapter/peripherals/ppu.tex | 105 -- chapter/peripherals/ppu.typ | 63 + chapter/peripherals/serial.tex | 55 - chapter/peripherals/serial.typ | 37 + common.typ | 55 + config.tex | 7 - gbctr.bib | 39 - gbctr.tex | 288 ---- gbctr.typ | 188 +++ gbctr.yml | 38 + images/DMG-CPU-pinout.pdf | Bin 21345 -> 0 bytes images/DMG-CPU-pinout.svg | 881 +++++++++++ images/MBC1-pinout.pdf | Bin 7668 -> 0 bytes images/MBC1-pinout.svg | 312 ++++ images/MBC2-pinout.pdf | Bin 8283 -> 0 bytes images/MBC2-pinout.svg | 327 +++++ images/MBC5-pinout.pdf | Bin 9904 -> 0 bytes images/MBC5-pinout.svg | 391 +++++ images/MGB-CPU-pinout.pdf | Bin 21256 -> 0 bytes images/MGB-CPU-pinout.svg | 879 +++++++++++ justfile | 16 + opcodes.toml | 624 ++++++++ preface.typ | 102 ++ third-party/UseLATEX.cmake | 2099 -------------------------- timing.typ | 171 +++ 69 files changed, 7421 insertions(+), 7043 deletions(-) delete mode 100644 .latexmkrc delete mode 100644 CMakeLists.txt delete mode 100644 appendix/external-bus.tex create mode 100644 appendix/external-bus.typ delete mode 100644 appendix/memory-map.tex create mode 100644 appendix/memory-map.typ delete mode 100644 appendix/opcode-tables.tex create mode 100644 appendix/opcode-tables.typ delete mode 100644 appendix/pinouts.tex create mode 100644 appendix/pinouts.typ delete mode 100644 chapter/cartridges/huc1.tex create mode 100644 chapter/cartridges/huc1.typ delete mode 100644 chapter/cartridges/huc3.tex create mode 100644 chapter/cartridges/huc3.typ delete mode 100644 chapter/cartridges/mbc1.tex create mode 100644 chapter/cartridges/mbc1.typ delete mode 100644 chapter/cartridges/mbc2.tex create mode 100644 chapter/cartridges/mbc2.typ delete mode 100644 chapter/cartridges/mbc3.tex create mode 100644 chapter/cartridges/mbc3.typ delete mode 100644 chapter/cartridges/mbc30.tex create mode 100644 chapter/cartridges/mbc30.typ delete mode 100644 chapter/cartridges/mbc5.tex create mode 100644 chapter/cartridges/mbc5.typ delete mode 100644 chapter/cartridges/mbc6.tex create mode 100644 chapter/cartridges/mbc6.typ delete mode 100644 chapter/cartridges/mbc7.tex create mode 100644 chapter/cartridges/mbc7.typ delete mode 100644 chapter/cartridges/mmm01.tex create mode 100644 chapter/cartridges/mmm01.typ delete mode 100644 chapter/cartridges/tama5.tex create mode 100644 chapter/cartridges/tama5.typ delete mode 100644 chapter/cpu/instruction-set.tex create mode 100644 chapter/cpu/instruction-set.typ delete mode 100644 chapter/cpu/timing.tex create mode 100644 chapter/cpu/timing.typ delete mode 100644 chapter/peripherals/boot-rom.tex create mode 100644 chapter/peripherals/boot-rom.typ delete mode 100644 chapter/peripherals/dma.tex create mode 100644 chapter/peripherals/dma.typ delete mode 100644 chapter/peripherals/p1.tex create mode 100644 chapter/peripherals/p1.typ delete mode 100644 chapter/peripherals/ppu.tex create mode 100644 chapter/peripherals/ppu.typ delete mode 100644 chapter/peripherals/serial.tex create mode 100644 chapter/peripherals/serial.typ create mode 100644 common.typ delete mode 100644 config.tex delete mode 100644 gbctr.bib delete mode 100644 gbctr.tex create mode 100644 gbctr.typ create mode 100644 gbctr.yml delete mode 100644 images/DMG-CPU-pinout.pdf create mode 100644 images/DMG-CPU-pinout.svg delete mode 100644 images/MBC1-pinout.pdf create mode 100644 images/MBC1-pinout.svg delete mode 100644 images/MBC2-pinout.pdf create mode 100644 images/MBC2-pinout.svg delete mode 100644 images/MBC5-pinout.pdf create mode 100644 images/MBC5-pinout.svg delete mode 100644 images/MGB-CPU-pinout.pdf create mode 100644 images/MGB-CPU-pinout.svg create mode 100644 justfile create mode 100644 opcodes.toml create mode 100644 preface.typ delete mode 100644 third-party/UseLATEX.cmake create mode 100644 timing.typ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1193c19..01783b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,6 @@ name: ci on: - pull_request: - push: - branches: - - main + - push concurrency: group: ${{ github.ref }} @@ -22,35 +19,29 @@ jobs: - name: Update Ubuntu package lists run: sudo apt update - name: Install Ubuntu packages - run: > + run: > sudo apt install - cmake - lmodern - python3-pygments - texlive-base - texlive-fonts-extra - texlive-fonts-recommended - texlive-lang-english - texlive-latex-base - texlive-latex-extra - texlive-latex-recommended - texlive-luatex - texlive-pictures - - name: Run cmake - run: cmake . - - name: Run make - run: make - - name: Upload log - uses: actions/upload-artifact@v3 - if: failure() - with: - name: log - path: build/gbctr.log + fonts-anonymous-pro + fonts-noto-core + - name: Install tools + run: | + mkdir -p "$HOME/.local/bin" + cd "$HOME/.local/bin" + curl -L https://github.com/typst/typst/releases/download/v0.10.0/typst-x86_64-unknown-linux-musl.tar.xz | tar -xJ --strip-components=1 typst-x86_64-unknown-linux-musl/typst + curl -L https://github.com/casey/just/releases/download/1.22.0/just-1.22.0-x86_64-unknown-linux-musl.tar.gz | tar -xz just + echo "$HOME/.local/bin" >> $GITHUB_PATH + - name: Install Font Awesome + run: | + mkdir -p "$HOME/.fonts" + curl -L https://github.com/FortAwesome/Font-Awesome/releases/download/6.5.1/fontawesome-free-6.5.1-desktop.zip -o fontawesome.zip + unzip -j fontawesome.zip 'fontawesome-free-6.5.1-desktop/otfs/*' -d "$HOME/.fonts" + fc-cache + - run: just build - name: Upload built PDF - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: pdf - path: build/gbctr.pdf + path: gbctr.pdf deploy: name: Deploy PDF needs: [build] @@ -68,10 +59,10 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: eu-west-1 - name: Download built PDF - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: pdf - path: build + path: gbctr.pdf - name: Upload to S3 run: | - aws s3 cp build/gbctr.pdf s3://gbdocs.gekkio.fi/gbctr.pdf + aws s3 cp gbctr.pdf s3://gbdocs.gekkio.fi/gbctr.pdf diff --git a/.gitignore b/.gitignore index cbd47af..421bb58 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,5 @@ *.log *.swp -build/ -CMakeCache.txt -CMakeFiles -Makefile -cmake_install.cmake +/gbctr.pdf +/config.json diff --git a/.latexmkrc b/.latexmkrc deleted file mode 100644 index f4daaed..0000000 --- a/.latexmkrc +++ /dev/null @@ -1,2 +0,0 @@ -$out_dir = "build"; -$xelatex = 'xelatex %O -shell-escape %S'; diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index ea66cc0..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,78 +0,0 @@ -cmake_minimum_required(VERSION 2.8.4) - -project(gbctr) - -set(LATEX_OUTPUT_PATH build) -set(PDFLATEX_COMPILER lualatex) -set(LATEX_COMPILER_FLAGS "-interaction=batchmode -file-line-error -shell-escape" CACHE STRING "Flags passed to latex") - -find_package(Git QUIET REQUIRED) - -set(DRAFT 1) -if(DEFINED ENV{GITHUB_REF}) - if($ENV{GITHUB_REF} STREQUAL "refs/heads/main") - set(DRAFT 0) - endif() -endif() - -if(${DRAFT} EQUAL 1) - execute_process( - COMMAND "${GIT_EXECUTABLE}" symbolic-ref --short HEAD - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_VARIABLE REVISION_SYMBOLIC - OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process( - COMMAND "${GIT_EXECUTABLE}" rev-list --count HEAD - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_VARIABLE REVISION_COUNT - OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process( - COMMAND "${GIT_EXECUTABLE}" rev-parse --short HEAD - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_VARIABLE REVISION_HASH - OUTPUT_STRIP_TRAILING_WHITESPACE) - set(REVISION ${REVISION_SYMBOLIC}-${REVISION_COUNT}[${REVISION_HASH}]) -else() - execute_process( - COMMAND "${GIT_EXECUTABLE}" rev-list --count main - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_VARIABLE REVISION - OUTPUT_STRIP_TRAILING_WHITESPACE) -endif() - -include(third-party/UseLATEX.cmake) - -add_latex_document(gbctr.tex - INPUTS - config.tex - chapter/cpu/instruction-set.tex - chapter/cpu/timing.tex - chapter/peripherals/boot-rom.tex - chapter/peripherals/dma.tex - chapter/peripherals/ppu.tex - chapter/peripherals/p1.tex - chapter/peripherals/serial.tex - chapter/cartridges/huc1.tex - chapter/cartridges/huc3.tex - chapter/cartridges/mbc1.tex - chapter/cartridges/mbc2.tex - chapter/cartridges/mbc3.tex - chapter/cartridges/mbc30.tex - chapter/cartridges/mbc5.tex - chapter/cartridges/mbc6.tex - chapter/cartridges/mbc7.tex - chapter/cartridges/mmm01.tex - chapter/cartridges/tama5.tex - appendix/external-bus.tex - appendix/memory-map.tex - appendix/opcode-tables.tex - appendix/pinouts.tex - code-snippets/mbc1_rom_dump.py - code-snippets/mbc2_rom_dump.py - images/DMG-CPU-pinout.pdf - images/MGB-CPU-pinout.pdf - images/MBC1-pinout.pdf - images/MBC2-pinout.pdf - images/MBC5-pinout.pdf - CONFIGURE config.tex - BIBFILES gbctr.bib) diff --git a/appendix/external-bus.tex b/appendix/external-bus.tex deleted file mode 100644 index 7fb3bc4..0000000 --- a/appendix/external-bus.tex +++ /dev/null @@ -1,227 +0,0 @@ -%!TEX root = ../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{Game Boy external bus} - -\section{Bus timings} - -\begin{figure}[H] - \centering - \begin{tikztimingtable} - CLK 4MHz & L 9{C} \\ - PHI 1MHz & L 4C 4C C \\ - A0-A14 & 10U \\ - RD & E 9L \\ - WR & 10H \\ - A15 & E 8H E \\ - CS & E 8H E \\ - Data & X 9Z \\ - \extracode - \tablegrid[opacity=0.2] - \tablerules - \begin{background} - \vertlines[opacity=0.5]{1, 9} - \end{background} - \end{tikztimingtable} - \caption{External bus idle machine cycle} -\end{figure} - -\begin{figure}[H] - \centering - \begin{subfigure}{0.4\linewidth} - \begin{tikztimingtable} - CLK 4MHz & L 9{C} \\ - PHI 1MHz & L 4C 4C C \\ - A0-A14 & 2U 7D{addr} U \\ - RD & E 9L \\ - WR & 10H \\ - A15 & E 2H 6L E \\ - CS & E 8H E \\ - Data & X 2Z 6D{data} Z \\ - \extracode - \tablegrid[opacity=0.2] - \tablerules - \begin{background} - \vertlines[opacity=0.5]{1, 9} - \end{background} - \draw[opacity=0.5, olive, thick] (7, 1.5) -- (7, -14.5); - \end{tikztimingtable} - \caption[0x0000-0x7FFF]{\hexrange{0000}{7FFF}\footnotemark} - \vspace{1cm} - \end{subfigure} - \begin{subfigure}{0.4\linewidth} - \begin{tikztimingtable} - CLK 4MHz & L 9{C} \\ - PHI 1MHz & L 4C 4C C \\ - A0-A14 & 2U 7D{addr} U \\ - RD & E 9L \\ - WR & 10H \\ - A15 & E 8H E \\ - CS & E 2H 6L E \\ - Data & X 2Z 6D{data} Z \\ - \extracode - \tablegrid[opacity=0.2] - \tablerules - \begin{background} - \vertlines[opacity=0.5]{1, 9} - \end{background} - \draw[opacity=0.5, olive, thick] (7, 1.5) -- (7, -14.5); - \end{tikztimingtable} - \caption{\hexrange{A000}{FDFF}} - \vspace{1cm} - \end{subfigure} - \begin{subfigure}{0.4\linewidth} - \begin{tikztimingtable} - CLK 4MHz & L 9{C} \\ - PHI 1MHz & L 4C 4C C \\ - A0-A14 & 2U 7D{addr} U \\ - RD & E 9L \\ - WR & 10H \\ - A15 & E 8H E \\ - CS & E 8H E \\ - Data & X 9Z \\ - \extracode - \tablegrid[opacity=0.2] - \tablerules - \begin{background} - \vertlines[opacity=0.5]{1, 9} - \end{background} - \end{tikztimingtable} - \caption{\hexrange{FE00}{FFFF}} - \vspace{1cm} - \end{subfigure} - \caption{External bus CPU read machine cycles} -\end{figure} - -\footnotetext{ - Does not apply to \hexrange{0000}{00FF} reads while the boot ROM is - enabled. Boot ROM accesses do not affect the external bus, so it is in the - idle state. -} - -\begin{figure}[H] - \centering - \begin{subfigure}{0.4\linewidth} - \begin{tikztimingtable} - CLK 4MHz & L 9{C} \\ - PHI 1MHz & L 4C 4C C \\ - A0-A14 & 2U 7D{addr} U \\ - RD & E L 7H L \\ - WR & 5H 3L 2H \\ - A15 & E 2H 6L E \\ - CS & E 8H E \\ - Data & X 4Z 4D{data} Z \\ - \extracode - \tablegrid[opacity=0.2] - \tablerules - \begin{background} - \vertlines[opacity=0.5]{1, 9} - \end{background} - \end{tikztimingtable} - \caption[0x0000-0x7FFF]{\hexrange{0000}{7FFF}\footnotemark} - \vspace{1cm} - \end{subfigure} - \begin{subfigure}{0.4\linewidth} - \begin{tikztimingtable} - CLK 4MHz & L 9{C} \\ - PHI 1MHz & L 4C 4C C \\ - A0-A14 & 2U 7D{addr} U \\ - RD & E L 7H L \\ - WR & 5H 3L 2H \\ - A15 & E 8H E \\ - CS & E 2H 6L E \\ - Data & X 4Z 4D{data} Z \\ - \extracode - \tablegrid[opacity=0.2] - \tablerules - \begin{background} - \vertlines[opacity=0.5]{1, 9} - \end{background} - \end{tikztimingtable} - \caption{\hexrange{A000}{FDFF}} - \vspace{1cm} - \end{subfigure} - \begin{subfigure}{0.4\linewidth} - \begin{tikztimingtable} - CLK 4MHz & L 9{C} \\ - PHI 1MHz & L 4C 4C C \\ - A0-A14 & 2U 7D{addr} U \\ - RD & E 9L \\ - WR & 10H \\ - A15 & E 8H E \\ - CS & E 8H E \\ - Data & X 9Z \\ - \extracode - \tablegrid[opacity=0.2] - \tablerules - \begin{background} - \vertlines[opacity=0.5]{1, 9} - \end{background} - \end{tikztimingtable} - \caption{\hexrange{FE00}{FFFF}} - \vspace{1cm} - \end{subfigure} - \caption{External bus timings for CPU write cycles} -\end{figure} - -\footnotetext{ - Does not apply to \hexrange{0000}{00FF} writes while the boot ROM is - enabled. Boot ROM accesses do not affect the external bus, so it is in the - idle state. -} - -\begin{figure}[H] - \centering - \begin{subfigure}{0.4\linewidth} - \begin{tikztimingtable} - CLK 4MHz & L 9{C} \\ - PHI 1MHz & L 4C 4C C \\ - A0-A14 & U 8D{addr} U \\ - RD & E 9L \\ - WR & 10H \\ - A15 & E 8L E \\ - CS & E 8H E \\ - Data & X 8D{data} Z \\ - \extracode - \tablegrid[opacity=0.2] - \tablerules - \begin{background} - \vertlines[opacity=0.5]{1, 9} - \end{background} - \end{tikztimingtable} - \caption[0x0000-0x7FFF]{\hexrange{0000}{7FFF}\footnotemark} - \vspace{1cm} - \end{subfigure} - \begin{subfigure}{0.4\linewidth} - \begin{tikztimingtable} - CLK 4MHz & L 9{C} \\ - PHI 1MHz & L 4C 4C C \\ - A0-A14 & U 8D{addr} U \\ - RD & E 9L \\ - WR & 10H \\ - A15 & E 8H E \\ - CS & E 8L E \\ - Data & X 8D{data} Z \\ - \extracode - \tablegrid[opacity=0.2] - \tablerules - \begin{background} - \vertlines[opacity=0.5]{1, 9} - \end{background} - \end{tikztimingtable} - \caption{\hexrange{A000}{FFFF}} - \vspace{1cm} - \end{subfigure} - \caption{External bus timings for OAM DMA read cycles} -\end{figure} - -\footnotetext{ - Does not apply to \hexrange{0000}{00FF} accesses while the boot ROM is - enabled. Boot ROM accesses do not affect the external bus, so it is in the - idle state. -} - -\end{document} diff --git a/appendix/external-bus.typ b/appendix/external-bus.typ new file mode 100644 index 0000000..57b83f3 --- /dev/null +++ b/appendix/external-bus.typ @@ -0,0 +1,227 @@ +#import "../common.typ": * +#import "../timing.typ" + +== Game Boy external bus + +=== Bus timings + +#let bootrom-footnote = footnote[ + Does not apply to #hex-range("0000", "00FF") reads while the boot ROM is enabled. Boot ROM accesses do not affect the external bus, so it is in the idle state. +] + +#let bus-diagram = (addr: array, rd: array, wr: array, a15: array, cs: array, data: array, sampling-edge: false) => { + import timing: * + text(13pt, + diagram( + grid: true, + (label: "CLK 4 MiHz", wave: ( + l(1), + ..range(9).map(_ => c(1)).flatten() + )), + (label: "PHI 1 MiHz", wave: (l(1), c(4), c(4), c(1),)), + (label: "A0-A14", wave: addr), + (label: "RD", wave: rd), + (label: "WR", wave: wr), + (label: "A15", wave: a15), + (label: "CS", wave: cs), + (label: "Data", wave: data), + fg: () => { + import cetz.draw + draw.set-style(stroke: (paint: rgb("#00000080"), thickness: 0.01em)) + draw.line((1.0, -0.5), (1.0, 15.5)) + draw.line((9.0, -0.5), (9.0, 15.5)) + if sampling-edge { + draw.line((7, -0.5), (7, 15.5), stroke: (paint: rgb("#80800080"), thickness: 0.8pt)) + } + } + ) + ) +} + +#figure( + { + import timing: * + bus-diagram( + addr: (u(10),), + rd: (e(1), l(9),), + wr: (h(10),), + a15: (e(1), h(8), e(1),), + cs: (e(1), h(8), e(1),), + data: (x(1), z(9),), + ) + }, + caption: "External bus idle machine cycle" +) + +#v(1cm) + +#figure( + { + import timing: * + [ + #columns(2, [ + #block( + breakable: false, + [ + #bus-diagram( + addr: (u(2), d(7, "addr"), u(1),), + rd: (e(1), l(9),), + wr: (h(10),), + a15: (e(1), h(2), l(6), e(1),), + cs: (e(1), h(8), e(1),), + data: (x(1), z(2), d(6, "data"), z(1)), + sampling-edge: true + ) + #align(right, [ + a) #hex-range("0000", "7FFF") + #bootrom-footnote + ]) + ] + ) + #colbreak() + #block( + breakable: false, + [ + #bus-diagram( + addr: (u(2), d(7, "addr"), u(1),), + rd: (e(1), l(9),), + wr: (h(10),), + a15: (e(1), h(8), e(1),), + cs: (e(1), h(2), l(6), e(1),), + data: (x(1), z(2), d(6, "data"), z(1)), + sampling-edge: true, + ) + #align(right, [ + b) #hex-range("A000", "FDFF") + ]) + ] + ) + ]) + #block( + breakable: false, + [ + #bus-diagram( + addr: (u(2), d(7, "addr"), u(1),), + rd: (e(1), l(9),), + wr: (h(10),), + a15: (e(1), h(8), e(1),), + cs: (e(1), h(8), e(1),), + data: (x(1), z(9),), + ) + #align(right, [ + c) #hex-range("FE00", "FFFF") + ]) + #v(1cm) + ] + ) + ] + }, + caption: "External bus CPU read machine cycles" +) +#figure( + { + import timing: * + [ + #columns(2, [ + #block( + breakable: false, + [ + #bus-diagram( + addr: (u(2), d(7, "addr"), u(1),), + rd: (e(1), l(1), h(7), l(1),), + wr: (h(5), l(3), h(2),), + a15: (e(1), h(2), l(6), e(1),), + cs: (e(1), h(8), e(1),), + data: (x(1), z(4), d(4, "data"), z(1)), + ) + #align(right, [ + a) #hex-range("0000", "7FFF") + #bootrom-footnote + ]) + ] + ) + #colbreak() + #block( + breakable: false, + [ + #bus-diagram( + addr: (u(2), d(7, "addr"), u(1),), + rd: (e(1), l(1), h(7), l(1),), + wr: (h(5), l(3), h(2),), + a15: (e(1), h(8), e(1),), + cs: (e(1), h(2), l(6), e(1),), + data: (x(1), z(4), d(4, "data"), z(1)), + ) + #align(right, [ + b) #hex-range("A000", "FDFF") + ]) + ] + ) + ]) + #block( + breakable: false, + [ + #bus-diagram( + addr: (u(2), d(7, "addr"), u(1),), + rd: (e(1), l(9),), + wr: (h(10),), + a15: (e(1), h(8), e(1),), + cs: (e(1), h(8), e(1),), + data: (x(1), z(9),), + ) + #align(right, [ + c) #hex-range("FE00", "FFFF") + ]) + #v(1cm) + ] + ) + ] + }, + caption: "External bus CPU write machine cycles" +) + +#figure( + { + import timing: * + [ + #columns(2, [ + #block( + breakable: false, + [ + #bus-diagram( + addr: (u(1), d(8, "addr"), u(1),), + rd: (e(1), l(9),), + wr: (h(10),), + a15: (e(1), l(8), e(1),), + cs: (e(1), h(8), e(1),), + data: (x(1), d(8, "data"), z(1)), + ) + #align(right, [ + a) #hex-range("0000", "7FFF") + #bootrom-footnote + ]) + ] + ) + #colbreak() + #block( + breakable: false, + [ + #bus-diagram( + addr: (u(1), d(8, "addr"), u(1),), + rd: (e(1), l(9),), + wr: (h(10),), + a15: (e(1), h(8), e(1),), + cs: (e(1), l(8), e(1),), + data: (x(1), d(8, "data"), z(1)), + ) + #align(right, [ + b) #hex-range("A000", "FFFF") + ]) + ] + ) + ]) + #v(1cm) + ] + }, + caption: "External bus timings for OAM DMA read machine cycles" +) diff --git a/appendix/memory-map.tex b/appendix/memory-map.tex deleted file mode 100644 index 42defc7..0000000 --- a/appendix/memory-map.tex +++ /dev/null @@ -1,331 +0,0 @@ -%!TEX root = ../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{Memory map tables} - -\begin{landscape} - -\newcommand{\unmappedbit}{\cellcolor{LightGray}} -\newcommand{\unmappedbyte}{\multicolumn{8}{c|}{\cellcolor{LightGray}}} -\newcommand{\gbcbit}{\cellcolor{LightYellow}} - -\begin{table} - \begin{center} - \caption{\hex{FFxx} registers: \hexrange{FF00}{FF1F}} - \ttfamily - \begin{tabularx}{\linewidth}{|l|C|C|C|C|C|C|C|C|} - \hline - & bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - 0xFF00~P1 & \unmappedbit & \unmappedbit & P15 {\scriptsize buttons} & P14 {\scriptsize d-pad} & P13 {\scriptsize \faArrowCircleDown~start} & P12 {\scriptsize \faArrowCircleUp~select} & P11 {\scriptsize \faArrowCircleLeft~B} & P10 {\scriptsize \faArrowCircleRight~A} \\ - \hline - 0xFF01~SB & \multicolumn{8}{c|}{SB<7:0>} \\ - \hline - 0xFF02~SC & SIO\_EN & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & \gbcbit SIO\_FAST & SIO\_CLK \\ - \hline - 0xFF03 & \unmappedbyte \\ - \hline - 0xFF04~DIV & \multicolumn{8}{c|}{DIVH<7:0>} \\ - \hline - 0xFF05~TIMA & \multicolumn{8}{c|}{TIMA<7:0>} \\ - \hline - 0xFF06~TMA & \multicolumn{8}{c|}{TMA<7:0>} \\ - \hline - 0xFF07~TAC & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & TAC\_EN & \multicolumn{2}{c|}{TAC\_CLK<1:0>} \\ - \hline - 0xFF08 & \unmappedbyte \\ - \hline - 0xFF09 & \unmappedbyte \\ - \hline - 0xFF0A & \unmappedbyte \\ - \hline - 0xFF0B & \unmappedbyte \\ - \hline - 0xFF0C & \unmappedbyte \\ - \hline - 0xFF0D & \unmappedbyte \\ - \hline - 0xFF0E & \unmappedbyte \\ - \hline - 0xFF0F~IF & \unmappedbit & \unmappedbit & \unmappedbit & IF\_JOYPAD & IF\_SERIAL & IF\_TIMER & IF\_STAT & IF\_VBLANK \\ - \hline - 0xFF10~NR10 & & & & & & & & \\ - \hline - 0xFF11~NR11 & & & & & & & & \\ - \hline - 0xFF12~NR12 & & & & & & & & \\ - \hline - 0xFF13~NR13 & & & & & & & & \\ - \hline - 0xFF14~NR14 & & & & & & & & \\ - \hline - 0xFF15 & \unmappedbyte \\ - \hline - 0xFF16~NR21 & & & & & & & & \\ - \hline - 0xFF17~NR22 & & & & & & & & \\ - \hline - 0xFF18~NR23 & & & & & & & & \\ - \hline - 0xFF19~NR24 & & & & & & & & \\ - \hline - 0xFF1A~NR30 & & & & & & & & \\ - \hline - 0xFF1B~NR31 & & & & & & & & \\ - \hline - 0xFF1C~NR32 & & & & & & & & \\ - \hline - 0xFF1D~NR33 & & & & & & & & \\ - \hline - 0xFF1E~NR34 & & & & & & & & \\ - \hline - 0xFF1F & \unmappedbyte \\ - \hline - & bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - \end{center} -\end{table} - -\begin{table} - \begin{center} - \caption{\hex{FFxx} registers: \hexrange{FF20}{FF3F}} - \ttfamily - \begin{tabularx}{\linewidth}{|l|C|C|C|C|C|C|C|C|} - \hline - & bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - 0xFF20~NR41 & & & & & & & & \\ - \hline - 0xFF21~NR42 & & & & & & & & \\ - \hline - 0xFF22~NR43 & & & & & & & & \\ - \hline - 0xFF23~NR44 & & & & & & & & \\ - \hline - 0xFF24~NR50 & & & & & & & & \\ - \hline - 0xFF25~NR51 & & & & & & & & \\ - \hline - 0xFF26~NR52 & & & & & & & & \\ - \hline - 0xFF27 & \unmappedbyte \\ - \hline - 0xFF28 & \unmappedbyte \\ - \hline - 0xFF29 & \unmappedbyte \\ - \hline - 0xFF2A & \unmappedbyte \\ - \hline - 0xFF2B & \unmappedbyte \\ - \hline - 0xFF2C & \unmappedbyte \\ - \hline - 0xFF2D & \unmappedbyte \\ - \hline - 0xFF2E & \unmappedbyte \\ - \hline - 0xFF2F & \unmappedbyte \\ - \hline - 0xFF30~WAV00 & & & & & & & & \\ - \hline - 0xFF31~WAV01 & & & & & & & & \\ - \hline - 0xFF32~WAV02 & & & & & & & & \\ - \hline - 0xFF33~WAV03 & & & & & & & & \\ - \hline - 0xFF34~WAV04 & & & & & & & & \\ - \hline - 0xFF35~WAV05 & & & & & & & & \\ - \hline - 0xFF36~WAV06 & & & & & & & & \\ - \hline - 0xFF37~WAV07 & & & & & & & & \\ - \hline - 0xFF38~WAV08 & & & & & & & & \\ - \hline - 0xFF39~WAV09 & & & & & & & & \\ - \hline - 0xFF3A~WAV10 & & & & & & & & \\ - \hline - 0xFF3B~WAV11 & & & & & & & & \\ - \hline - 0xFF3C~WAV12 & & & & & & & & \\ - \hline - 0xFF3D~WAV13 & & & & & & & & \\ - \hline - 0xFF3E~WAV14 & & & & & & & & \\ - \hline - 0xFF3F~WAV15 & & & & & & & & \\ - \hline - & bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - \end{center} -\end{table} - -\begin{table} - \begin{center} - \caption{\hex{FFxx} registers: \hexrange{FF40}{FF5F}} - \ttfamily - \begin{tabularx}{\linewidth}{|l|C|C|C|C|C|C|C|C|} - \hline - & bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - 0xFF40~LCDC & LCD\_EN & WIN\_MAP & WIN\_EN & TILE\_SEL & BG\_MAP & OBJ\_SIZE & OBJ\_EN & BG\_EN \\ - \hline - 0xFF41~STAT & \unmappedbit & INTR\_LYC & INTR\_M2 & INTR\_M1 & INTR\_M0 & LYC\_STAT & \multicolumn{2}{c|}{LCD\_MODE<1:0>} \\ - \hline - 0xFF42~SCY & & & & & & & & \\ - \hline - 0xFF43~SCX & & & & & & & & \\ - \hline - 0xFF44~LY & & & & & & & & \\ - \hline - 0xFF45~LYC & & & & & & & & \\ - \hline - 0xFF46~DMA & \multicolumn{8}{c|}{DMA<7:0>} \\ - \hline - 0xFF47~BGP & & & & & & & & \\ - \hline - 0xFF48~OBP0 & & & & & & & & \\ - \hline - 0xFF49~OBP1 & & & & & & & & \\ - \hline - 0xFF4A~WY & & & & & & & & \\ - \hline - 0xFF4B~WX & & & & & & & & \\ - \hline - 0xFF4C~???? & \multicolumn{8}{c|}{} \\ - \hline - \gbcbit 0xFF4D~KEY1 & \gbcbit KEY1\_FAST & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & \gbcbit KEY1\_EN \\ - \hline - 0xFF4E & \unmappedbyte \\ - \hline - \gbcbit 0xFF4F~VBK & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & \multicolumn{2}{c|}{\gbcbit VBK<1:0>} \\ - \hline - 0xFF50~BOOT & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & BOOT\_OFF \\ - \hline - 0xFF51~HDMA1 & & & & & & & & \\ - \hline - 0xFF52~HDMA2 & & & & & & & & \\ - \hline - 0xFF53~HDMA3 & & & & & & & & \\ - \hline - 0xFF54~HDMA4 & & & & & & & & \\ - \hline - 0xFF55~HDMA5 & & & & & & & & \\ - \hline - 0xFF56~RP & & & & & & & & \\ - \hline - 0xFF57 & \unmappedbyte \\ - \hline - 0xFF58 & \unmappedbyte \\ - \hline - 0xFF59 & \unmappedbyte \\ - \hline - 0xFF5A & \unmappedbyte \\ - \hline - 0xFF5B & \unmappedbyte \\ - \hline - 0xFF5C & \unmappedbyte \\ - \hline - 0xFF5D & \unmappedbyte \\ - \hline - 0xFF5E & \unmappedbyte \\ - \hline - 0xFF5F & \unmappedbyte \\ - \hline - & bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - \end{center} -\end{table} - -\begin{table} - \begin{center} - \caption{\hex{FFxx} registers: \hexrange{FF60}{FF7F}, \hex{FFFF}} - \ttfamily - \begin{tabularx}{\linewidth}{|l|C|C|C|C|C|C|C|C|} - \hline - & bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - 0xFF60 & \unmappedbyte \\ - \hline - 0xFF61 & \unmappedbyte \\ - \hline - 0xFF62 & \unmappedbyte \\ - \hline - 0xFF63 & \unmappedbyte \\ - \hline - 0xFF64 & \unmappedbyte \\ - \hline - 0xFF65 & \unmappedbyte \\ - \hline - 0xFF66 & \unmappedbyte \\ - \hline - 0xFF67 & \unmappedbyte \\ - \hline - \gbcbit 0xFF68~BCPS & & & & & & & & \\ - \hline - \gbcbit 0xFF69~BCPD & & & & & & & & \\ - \hline - \gbcbit 0xFF6A~OCPS & & & & & & & & \\ - \hline - \gbcbit 0xFF6B~OCPD & & & & & & & & \\ - \hline - 0xFF6C~???? & \multicolumn{8}{c|}{} \\ - \hline - 0xFF6D & \unmappedbyte \\ - \hline - 0xFF6E & \unmappedbyte \\ - \hline - 0xFF6F & \unmappedbyte \\ - \hline - \gbcbit 0xFF70~SVBK & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & \unmappedbit & \multicolumn{2}{c|}{\gbcbit SVBK<1:0>}\\ - \hline - 0xFF71 & \unmappedbyte \\ - \hline - 0xFF72~???? & \multicolumn{8}{c|}{} \\ - \hline - 0xFF73~???? & \multicolumn{8}{c|}{} \\ - \hline - 0xFF74~???? & \multicolumn{8}{c|}{} \\ - \hline - 0xFF75~???? & \multicolumn{8}{c|}{} \\ - \hline - \gbcbit 0xFF76~PCM12 & \multicolumn{4}{c|}{\gbcbit PCM12\_CH2} & \multicolumn{4}{c|}{\gbcbit PCM12\_CH1} \\ - \hline - \gbcbit 0xFF77~PCM34 & \multicolumn{4}{c|}{\gbcbit PCM34\_CH4} & \multicolumn{4}{c|}{\gbcbit PCM34\_CH3} \\ - \hline - 0xFF78 & \unmappedbyte \\ - \hline - 0xFF79 & \unmappedbyte \\ - \hline - 0xFF7A & \unmappedbyte \\ - \hline - 0xFF7B & \unmappedbyte \\ - \hline - 0xFF7C & \unmappedbyte \\ - \hline - 0xFF7D & \unmappedbyte \\ - \hline - 0xFF7E & \unmappedbyte \\ - \hline - 0xFF7F & \unmappedbyte \\ - \hline - 0xFFFF~IE & \multicolumn{3}{c|}{IE\_UNUSED<2:0>} & IE\_JOYPAD & IE\_SERIAL & IE\_TIMER & IE\_STAT & IE\_VBLANK \\ - \hline - & bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - \end{center} -\end{table} - -\end{landscape} - -\end{document} diff --git a/appendix/memory-map.typ b/appendix/memory-map.typ new file mode 100644 index 0000000..5b67055 --- /dev/null +++ b/appendix/memory-map.typ @@ -0,0 +1,199 @@ +#import "../common.typ": * + +#let detail(..args) = text(7pt, ..args) + +#let gbc-bit(content) = cellx(fill: rgb("#FFFFED"), content) +#let gbc-bits(length, content) = colspanx(length, fill: rgb("#FFFFED"), content) + +#let unmapped-bit = cellx(fill: rgb("#D3D3D3"))[] +#let unmapped-bits(length) = range(length).map((_) => unmapped-bit) +#let unmapped-byte = colspanx(8, fill: rgb("D3D3D3"))[] +#let todo(length) = range(length).map((_) => []) +#set text(9pt) + +== Memory map tables + +#set page(flipped: true) + +#figure( + tablex( + columns: (auto, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr), + inset: (x: 5pt, y: 3.5pt), + align: (left, center, center, center, center, center, center, center, center), + [], [bit 7], [6], [5], [4], [3], [2], [1], [bit 0], + [#hex("FF00") P1], ..unmapped-bits(2), [P15 #detail[buttons]], [P14 #detail[d-pad]], [P13 #detail[#awesome("\u{f358}") start]], [P12 #detail[#awesome("\u{f35b}") select]], [P11 #detail[#awesome("\u{f359}") B]], [P10 #detail[#awesome("\u{f35a}") A]], + [#hex("FF01") SB], colspanx(8)[SB\<7:0\>], + [#hex("FF02") SC], [SIO_EN], ..unmapped-bits(5), gbc-bit[SIO_FAST], [SIO_CLK], + hex("FF03"), unmapped-byte, + [#hex("FF04") DIV], colspanx(8)[DIVH\<7:0\>], + [#hex("FF05") TIMA], colspanx(8)[TIMA\<7:0\>], + [#hex("FF06") TMA], colspanx(8)[TMA\<7:0\>], + [#hex("FF07") TAC], ..unmapped-bits(5), [TAC_EN], colspanx(2)[TAC_CLK\<1:0\>], + hex("FF08"), unmapped-byte, + hex("FF09"), unmapped-byte, + hex("FF0A"), unmapped-byte, + hex("FF0B"), unmapped-byte, + hex("FF0C"), unmapped-byte, + hex("FF0D"), unmapped-byte, + hex("FF0E"), unmapped-byte, + [#hex("FF0F") IF], ..unmapped-bits(3), [IF_JOYPAD], [IF_SERIAL], [IF_TIMER], [IF_STAT], [IF_VBLANK], + [#hex("FF10") NR10], ..todo(8), + [#hex("FF11") NR11], ..todo(8), + [#hex("FF12") NR12], ..todo(8), + [#hex("FF13") NR13], ..todo(8), + [#hex("FF14") NR14], ..todo(8), + hex("FF15"), unmapped-byte, + [#hex("FF16") NR21], ..todo(8), + [#hex("FF17") NR22], ..todo(8), + [#hex("FF18") NR23], ..todo(8), + [#hex("FF19") NR24], ..todo(8), + [#hex("FF1A") NR30], ..todo(8), + [#hex("FF1B") NR31], ..todo(8), + [#hex("FF1C") NR32], ..todo(8), + [#hex("FF1D") NR33], ..todo(8), + [#hex("FF1E") NR34], ..todo(8), + hex("FF1F"), unmapped-byte, + [], [bit 7], [6], [5], [4], [3], [2], [1], [bit 0], + ), + kind: table, + caption: [#hex[FFxx] registers: #hex-range("FF00", "FF1F")] +) + +#pagebreak() + +#figure( + tablex( + columns: (auto, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr), + inset: (x: 5pt, y: 3.5pt), + align: (left, center, center, center, center, center, center, center, center), + [], [bit 7], [6], [5], [4], [3], [2], [1], [bit 0], + [#hex("FF20") NR41], ..todo(8), + [#hex("FF21") NR42], ..todo(8), + [#hex("FF22") NR43], ..todo(8), + [#hex("FF23") NR44], ..todo(8), + [#hex("FF24") NR50], ..todo(8), + [#hex("FF25") NR51], ..todo(8), + [#hex("FF26") NR52], ..todo(8), + hex("FF27"), unmapped-byte, + hex("FF28"), unmapped-byte, + hex("FF29"), unmapped-byte, + hex("FF2A"), unmapped-byte, + hex("FF2B"), unmapped-byte, + hex("FF2C"), unmapped-byte, + hex("FF2D"), unmapped-byte, + hex("FF2E"), unmapped-byte, + hex("FF2F"), unmapped-byte, + [#hex("FF30") WAV00], ..todo(8), + [#hex("FF31") WAV01], ..todo(8), + [#hex("FF32") WAV02], ..todo(8), + [#hex("FF33") WAV03], ..todo(8), + [#hex("FF34") WAV04], ..todo(8), + [#hex("FF35") WAV05], ..todo(8), + [#hex("FF36") WAV06], ..todo(8), + [#hex("FF37") WAV07], ..todo(8), + [#hex("FF38") WAV08], ..todo(8), + [#hex("FF39") WAV09], ..todo(8), + [#hex("FF3A") WAV10], ..todo(8), + [#hex("FF3B") WAV11], ..todo(8), + [#hex("FF3C") WAV12], ..todo(8), + [#hex("FF3D") WAV13], ..todo(8), + [#hex("FF3E") WAV14], ..todo(8), + [#hex("FF3F") WAV15], ..todo(8), + [], [bit 7], [6], [5], [4], [3], [2], [1], [bit 0], + ), + kind: table, + caption: [#hex[FFxx] registers: #hex-range("FF20", "FF3F")] +) + +#pagebreak() + +#figure( + tablex( + columns: (auto, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr), + inset: (x: 5pt, y: 3.5pt), + align: (left, center, center, center, center, center, center, center, center), + [], [bit 7], [6], [5], [4], [3], [2], [1], [bit 0], + [#hex("FF40") LCDC], [LCD_EN], [WIN_MAP], [WIN_EN], [TILE_SEL], [BG_MAP], [OBJ_SIZE], [OBJ_EN], [BG_EN], + [#hex("FF41") STAT], unmapped-bit, [INTR_LYC], [INTR_M2], [INTR_M1], [INTR_M0], [LYC_STAT], colspanx(2)[LCD_MODE\<1:0\>], + [#hex("FF42") SCY], ..todo(8), + [#hex("FF43") SCX], ..todo(8), + [#hex("FF44") LY], ..todo(8), + [#hex("FF45") LYC], ..todo(8), + [#hex("FF46") DMA], colspanx(8)[DMA\<7:0\>], + [#hex("FF47") BGP], ..todo(8), + [#hex("FF48") OBP0], ..todo(8), + [#hex("FF49") OBP1], ..todo(8), + [#hex("FF4A") WY], ..todo(8), + [#hex("FF4B") WX], ..todo(8), + gbc-bit[#hex("FF4C") ????], ..todo(8), + gbc-bit[#hex("FF4D") KEY1], gbc-bit[KEY1_FAST], ..unmapped-bits(6), gbc-bit[KEY1_EN], + hex("FF4E"), unmapped-byte, + gbc-bit[#hex("FF4F") VBK], ..unmapped-bits(6), gbc-bits(2)[VBK\<1:0\>], + [#hex("FF50") BOOT], ..unmapped-bits(7), [BOOT_OFF], + gbc-bit[#hex("FF51") HDMA1], ..todo(8), + gbc-bit[#hex("FF52") HDMA2], ..todo(8), + gbc-bit[#hex("FF53") HDMA3], ..todo(8), + gbc-bit[#hex("FF54") HDMA4], ..todo(8), + gbc-bit[#hex("FF55") HDMA5], ..todo(8), + gbc-bit[#hex("FF56") RP], ..todo(8), + hex("FF57"), unmapped-byte, + hex("FF58"), unmapped-byte, + hex("FF59"), unmapped-byte, + hex("FF5A"), unmapped-byte, + hex("FF5B"), unmapped-byte, + hex("FF5C"), unmapped-byte, + hex("FF5D"), unmapped-byte, + hex("FF5E"), unmapped-byte, + hex("FF5F"), unmapped-byte, + [], [bit 7], [6], [5], [4], [3], [2], [1], [bit 0], + ), + kind: table, + caption: [#hex[FFxx] registers: #hex-range("FF40", "FF5F")] +) + +#pagebreak() + +#figure( + tablex( + columns: (auto, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr), + inset: (x: 5pt, y: 3.5pt), + align: (left, center, center, center, center, center, center, center, center), + [], [bit 7], [6], [5], [4], [3], [2], [1], [bit 0], + [#hex("FF60") ????], ..unmapped-bits(6), [], [], + hex("FF61"), unmapped-byte, + hex("FF62"), unmapped-byte, + hex("FF63"), unmapped-byte, + hex("FF64"), unmapped-byte, + hex("FF65"), unmapped-byte, + hex("FF66"), unmapped-byte, + hex("FF67"), unmapped-byte, + gbc-bit[#hex("FF68") BCPS], ..todo(8), + gbc-bit[#hex("FF69") BPCD], ..todo(8), + gbc-bit[#hex("FF6A") OCPS], ..todo(8), + gbc-bit[#hex("FF6B") OCPD], ..todo(8), + gbc-bit[#hex("FF6C") ????], ..todo(8), + hex("FF6D"), unmapped-byte, + hex("FF6E"), unmapped-byte, + hex("FF6F"), unmapped-byte, + gbc-bit[#hex("FF70") SVBK], ..unmapped-bits(6), gbc-bits(2)[SVBK\<1:0\>], + hex("FF71"), unmapped-byte, + gbc-bit[#hex("FF72") ????], ..todo(8), + gbc-bit[#hex("FF73") ????], ..todo(8), + gbc-bit[#hex("FF74") ????], ..todo(8), + gbc-bit[#hex("FF75") ????], ..todo(8), + gbc-bit[#hex("FF76") PCM12], gbc-bits(4)[PCM12_CH2], gbc-bits(4)[PCM12_CH1], + gbc-bit[#hex("FF77") PCM34], gbc-bits(4)[PCM34_CH4], gbc-bits(4)[PCM34_CH3], + hex("FF78"), unmapped-byte, + hex("FF79"), unmapped-byte, + hex("FF7A"), unmapped-byte, + hex("FF7B"), unmapped-byte, + hex("FF7C"), unmapped-byte, + hex("FF7D"), unmapped-byte, + hex("FF7E"), unmapped-byte, + hex("FF7F"), unmapped-byte, + [#hex("FFFF") IE], colspanx(3)[IE_UNUSED\<2:0\>], [IE_JOYPAD], [IE_SERIAL], [IE_TIMER], [IE_STAT], [IE_VBLANK], + [], [bit 7], [6], [5], [4], [3], [2], [1], [bit 0], + ), + kind: table, + caption: [#hex[FFxx] registers: #hex-range("FF60", "FF7F"), #hex("FFFF")] +) diff --git a/appendix/opcode-tables.tex b/appendix/opcode-tables.tex deleted file mode 100644 index 2920469..0000000 --- a/appendix/opcode-tables.tex +++ /dev/null @@ -1,134 +0,0 @@ -%!TEX root = ../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{Instruction set tables} - -These tables include all the opcodes in the Sharp SM83 instruction set. The -style and layout of these tables was inspired by the opcode tables available at -pastraiser.com \cite{pastraiser}. - -\begin{landscape} - -\newcommand{\oplb}{\cellcolor{SkyBlue}} -\newcommand{\oplw}{\cellcolor{PaleGreen}} -\newcommand{\opab}{\cellcolor{Gold}} -\newcommand{\opaw}{\cellcolor{Pink}} -\newcommand{\opbi}{\cellcolor{Turquoise}} -\newcommand{\opcf}{\cellcolor{SandyBrown}} -\newcommand{\opmi}{\cellcolor{PaleVioletRed}} -\newcommand{\opun}{\cellcolor{Silver}} - -\begin{table} - \begin{center} - \fontsize{5.7pt}{13pt} - \fcolorbox{Black}{SkyBlue}{8-bit loads} - \fcolorbox{Black}{PaleGreen}{16-bit loads} - \fcolorbox{Black}{Gold}{8-bit arithmetic/logical} - \fcolorbox{Black}{Pink}{16-bit arithmetic} - \fcolorbox{Black}{Turquoise}{Rotates, shifts, and bit operations} - \fcolorbox{Black}{SandyBrown}{Control flow} - \fcolorbox{Black}{PaleVioletRed}{Miscellaneous} - \fcolorbox{Black}{Silver}{Undefined} - \caption{Sharp SM83 instruction set} - \ttfamily\bfseries - \begin{tabularx}{\linewidth}{|l|C|C|C|C|C|C|C|C|C|C|C|C|C|C|C|C|} - \hline - & x0 & x1 & x2 & x3 & x4 & x5 & x6 & x7 & x8 & x9 & xA & xB & xC & xD & xE & xF \\ - \hline - 0x & \opmi \hyperref[inst:NOP]{NOP} & \oplw \hyperref[inst:LD_rr_nn]{LD BC,nn} & \oplb \hyperref[inst:LD_bc_a]{LD (BC),A} & \opaw INC BC & \opab \hyperref[inst:INC_r]{INC B} & \opab \hyperref[inst:DEC_r]{DEC B} & \oplb \hyperref[inst:LD_r_n]{LD B,n} & \opbi RLCA & \oplw \hyperref[inst:LD_nn_sp]{LD (nn),SP} & \opaw ADD HL,BC & \oplb \hyperref[inst:LD_a_bc]{LD A,(BC)} & \opaw DEC BC & \opab \hyperref[inst:INC_r]{INC C} & \opab \hyperref[inst:DEC_r]{DEC C} & \oplb \hyperref[inst:LD_r_n]{LD C,n} & \opbi RRCA \\ - \hline - 1x & \opmi \hyperref[inst:STOP]{STOP} & \oplw \hyperref[inst:LD_rr_nn]{LD DE,nn} & \oplb \hyperref[inst:LD_de_a]{LD (DE),A} & \opaw INC DE & \opab \hyperref[inst:INC_r]{INC D} & \opab \hyperref[inst:DEC_r]{DEC D} & \oplb \hyperref[inst:LD_r_n]{LD D,n} & \opbi RLA & \opcf \hyperref[inst:JR]{JR e} & \opaw ADD HL,DE & \oplb \hyperref[inst:LD_a_de]{LD A,(DE)} & \opaw DEC DE & \opab \hyperref[inst:INC_r]{INC E} & \opab \hyperref[inst:DEC_r]{DEC E} & \oplb \hyperref[inst:LD_r_n]{LD E,n} & \opbi RRA \\ - \hline - 2x & \opcf \hyperref[inst:JR_cc]{JR NZ,e} & \oplw \hyperref[inst:LD_rr_nn]{LD HL,nn} & \oplb \hyperref[inst:LD_hli_a]{LD (HL+),A} & \opaw INC HL & \opab \hyperref[inst:INC_r]{INC H} & \opab \hyperref[inst:DEC_r]{DEC H} & \oplb \hyperref[inst:LD_r_n]{LD H,n} & \opab \hyperref[inst:DAA]{DAA} & \opcf \hyperref[inst:JR_cc]{JR Z,e} & \opaw ADD HL,HL & \oplb \hyperref[inst:LD_a_hli]{LD A,(HL+)} & \opaw DEC HL & \opab \hyperref[inst:INC_r]{INC L} & \opab \hyperref[inst:DEC_r]{DEC L} & \oplb \hyperref[inst:LD_r_n]{LD L,n} & \opab \hyperref[inst:CPL]{CPL} \\ - \hline - 3x & \opcf \hyperref[inst:JR_cc]{JR NC,e} & \oplw \hyperref[inst:LD_rr_nn]{LD SP,nn} & \oplb \hyperref[inst:LD_hld_a]{LD (HL-),A} & \opaw INC SP & \opab \hyperref[inst:INC_hl]{INC (HL)} & \opab \hyperref[inst:DEC_hl]{DEC (HL)} & \oplb \hyperref[inst:LD_hl_n]{LD (HL),n} & \opab \hyperref[inst:SCF]{SCF} & \opcf \hyperref[inst:JR_cc]{JR C,e} & \opaw ADD HL,SP & \oplb \hyperref[inst:LD_a_hld]{LD A,(HL-)} & \opaw DEC SP & \opab \hyperref[inst:INC_r]{INC A} & \opab \hyperref[inst:DEC_r]{DEC A} & \oplb \hyperref[inst:LD_r_n]{LD A,n} & \opab \hyperref[inst:CCF]{CCF} \\ - \hline - 4x & \oplb \hyperref[inst:LD_r_r]{LD B,B} & \oplb \hyperref[inst:LD_r_r]{LD B,C} & \oplb \hyperref[inst:LD_r_r]{LD B,D} & \oplb \hyperref[inst:LD_r_r]{LD B,E} & \oplb \hyperref[inst:LD_r_r]{LD B,H} & \oplb \hyperref[inst:LD_r_r]{LD B,L} & \oplb \hyperref[inst:LD_r_hl]{LD B,(HL)} & \oplb \hyperref[inst:LD_r_r]{LD B,A} & \oplb \hyperref[inst:LD_r_r]{LD C,B} & \oplb \hyperref[inst:LD_r_r]{LD C,C} & \oplb \hyperref[inst:LD_r_r]{LD C,D} & \oplb \hyperref[inst:LD_r_r]{LD C,E} & \oplb \hyperref[inst:LD_r_r]{LD C,H} & \oplb \hyperref[inst:LD_r_r]{LD C,L} & \oplb \hyperref[inst:LD_r_hl]{LD C,(HL)} & \oplb \hyperref[inst:LD_r_r]{LD C,A} \\ - \hline - 5x & \oplb \hyperref[inst:LD_r_r]{LD D,B} & \oplb \hyperref[inst:LD_r_r]{LD D,C} & \oplb \hyperref[inst:LD_r_r]{LD D,D} & \oplb \hyperref[inst:LD_r_r]{LD D,E} & \oplb \hyperref[inst:LD_r_r]{LD D,H} & \oplb \hyperref[inst:LD_r_r]{LD D,L} & \oplb \hyperref[inst:LD_r_hl]{LD D,(HL)} & \oplb \hyperref[inst:LD_r_r]{LD D,A} & \oplb \hyperref[inst:LD_r_r]{LD E,B} & \oplb \hyperref[inst:LD_r_r]{LD E,C} & \oplb \hyperref[inst:LD_r_r]{LD E,D} & \oplb \hyperref[inst:LD_r_r]{LD E,E} & \oplb \hyperref[inst:LD_r_r]{LD E,H} & \oplb \hyperref[inst:LD_r_r]{LD E,L} & \oplb \hyperref[inst:LD_r_hl]{LD E,(HL)} & \oplb \hyperref[inst:LD_r_r]{LD E,A} \\ - \hline - 6x & \oplb \hyperref[inst:LD_r_r]{LD H,B} & \oplb \hyperref[inst:LD_r_r]{LD H,C} & \oplb \hyperref[inst:LD_r_r]{LD H,D} & \oplb \hyperref[inst:LD_r_r]{LD H,E} & \oplb \hyperref[inst:LD_r_r]{LD H,H} & \oplb \hyperref[inst:LD_r_r]{LD H,L} & \oplb \hyperref[inst:LD_r_hl]{LD H,(HL)} & \oplb \hyperref[inst:LD_r_r]{LD H,A} & \oplb \hyperref[inst:LD_r_r]{LD L,B} & \oplb \hyperref[inst:LD_r_r]{LD L,C} & \oplb \hyperref[inst:LD_r_r]{LD L,D} & \oplb \hyperref[inst:LD_r_r]{LD L,E} & \oplb \hyperref[inst:LD_r_r]{LD L,H} & \oplb \hyperref[inst:LD_r_r]{LD L,L} & \oplb \hyperref[inst:LD_r_hl]{LD L,(HL)} & \oplb \hyperref[inst:LD_r_r]{LD L,A} \\ - \hline - 7x & \oplb \hyperref[inst:LD_hl_r]{LD (HL),B} & \oplb \hyperref[inst:LD_hl_r]{LD (HL),C} & \oplb \hyperref[inst:LD_hl_r]{LD (HL),D} & \oplb \hyperref[inst:LD_hl_r]{LD (HL),E} & \oplb \hyperref[inst:LD_hl_r]{LD (HL),H} & \oplb \hyperref[inst:LD_hl_r]{LD (HL),L} & \opmi \hyperref[inst:HALT]{HALT} & \oplb \hyperref[inst:LD_hl_r]{LD (HL),A} & \oplb \hyperref[inst:LD_r_r]{LD A,B} & \oplb \hyperref[inst:LD_r_r]{LD A,C} & \oplb \hyperref[inst:LD_r_r]{LD A,D} & \oplb \hyperref[inst:LD_r_r]{LD A,E} & \oplb \hyperref[inst:LD_r_r]{LD A,H} & \oplb \hyperref[inst:LD_r_r]{LD A,L} & \oplb \hyperref[inst:LD_r_hl]{LD A,(HL)} & \oplb \hyperref[inst:LD_r_r]{LD A,A} \\ - \hline - 8x & \opab \hyperref[inst:ADD_r]{ADD B} & \opab \hyperref[inst:ADD_r]{ADD C} & \opab \hyperref[inst:ADD_r]{ADD D} & \opab \hyperref[inst:ADD_r]{ADD E} & \opab \hyperref[inst:ADD_r]{ADD H} & \opab \hyperref[inst:ADD_r]{ADD L} & \opab \hyperref[inst:ADD_hl]{ADD (HL)} & \opab \hyperref[inst:ADD_r]{ADD A} & \opab \hyperref[inst:ADC_r]{ADC B} & \opab \hyperref[inst:ADC_r]{ADC C} & \opab \hyperref[inst:ADC_r]{ADC D} & \opab \hyperref[inst:ADC_r]{ADC E} & \opab \hyperref[inst:ADC_r]{ADC H} & \opab \hyperref[inst:ADC_r]{ADC L} & \opab \hyperref[inst:ADC_hl]{ADC (HL)} & \opab \hyperref[inst:ADC_r]{ADC A} \\ - \hline - 9x & \opab \hyperref[inst:SUB_r]{SUB B} & \opab \hyperref[inst:SUB_r]{SUB C} & \opab \hyperref[inst:SUB_r]{SUB D} & \opab \hyperref[inst:SUB_r]{SUB E} & \opab \hyperref[inst:SUB_r]{SUB H} & \opab \hyperref[inst:SUB_r]{SUB L} & \opab \hyperref[inst:SUB_hl]{SUB (HL)} & \opab \hyperref[inst:SUB_r]{SUB A} & \opab \hyperref[inst:SBC_r]{SBC B} & \opab \hyperref[inst:SBC_r]{SBC C} & \opab \hyperref[inst:SBC_r]{SBC D} & \opab \hyperref[inst:SBC_r]{SBC E} & \opab \hyperref[inst:SBC_r]{SBC H} & \opab \hyperref[inst:SBC_r]{SBC L} & \opab \hyperref[inst:SBC_hl]{SBC (HL)} & \opab \hyperref[inst:SBC_r]{SBC A} \\ - \hline - Ax & \opab \hyperref[inst:AND_r]{AND B} & \opab \hyperref[inst:AND_r]{AND C} & \opab \hyperref[inst:AND_r]{AND D} & \opab \hyperref[inst:AND_r]{AND E} & \opab \hyperref[inst:AND_r]{AND H} & \opab \hyperref[inst:AND_r]{AND L} & \opab \hyperref[inst:AND_hl]{AND (HL)} & \opab \hyperref[inst:AND_r]{AND A} & \opab \hyperref[inst:XOR_r]{XOR B} & \opab \hyperref[inst:XOR_r]{XOR C} & \opab \hyperref[inst:XOR_r]{XOR D} & \opab \hyperref[inst:XOR_r]{XOR E} & \opab \hyperref[inst:XOR_r]{XOR H} & \opab \hyperref[inst:XOR_r]{XOR L} & \opab \hyperref[inst:XOR_hl]{XOR (HL)} & \opab \hyperref[inst:XOR_r]{XOR A} \\ - \hline - Bx & \opab \hyperref[inst:OR_r]{OR B} & \opab \hyperref[inst:OR_r]{OR C} & \opab \hyperref[inst:OR_r]{OR D} & \opab \hyperref[inst:OR_r]{OR E} & \opab \hyperref[inst:OR_r]{OR H} & \opab \hyperref[inst:OR_r]{OR L} & \opab \hyperref[inst:OR_hl]{OR (HL)} & \opab \hyperref[inst:OR_r]{OR A} & \opab \hyperref[inst:CP_r]{CP B} & \opab \hyperref[inst:CP_r]{CP C} & \opab \hyperref[inst:CP_r]{CP D} & \opab \hyperref[inst:CP_r]{CP E} & \opab \hyperref[inst:CP_r]{CP H} & \opab \hyperref[inst:CP_r]{CP L} & \opab \hyperref[inst:CP_hl]{CP (HL)} & \opab \hyperref[inst:CP_r]{CP A} \\ - \hline - Cx & \opcf \hyperref[inst:RET_cc]{RET NZ} & \oplw \hyperref[inst:POP_rr]{POP BC} & \opcf \hyperref[inst:JP_cc]{JP NZ,nn} & \opcf \hyperref[inst:JP]{JP nn} & \opcf \hyperref[inst:CALL_cc]{CALL NZ,nn} & \oplw \hyperref[inst:PUSH_rr]{PUSH BC} & \opab \hyperref[inst:ADD_n]{ADD n} & \opcf \hyperref[inst:RST]{RST 0x00} & \opcf \hyperref[inst:RET_cc]{RET Z} & \opcf \hyperref[inst:RET]{RET} & \opcf \hyperref[inst:JP_cc]{JP Z,nn} & \opbi CB op & \opcf \hyperref[inst:CALL_cc]{CALL Z,nn} & \opcf \hyperref[inst:CALL]{CALL nn} & \opab \hyperref[inst:ADC_n]{ADC n} & \opcf \hyperref[inst:RST]{RST 0x08} \\ - \hline - Dx & \opcf \hyperref[inst:RET_cc]{RET NC} & \oplw \hyperref[inst:POP_rr]{POP DE} & \opcf \hyperref[inst:JP_cc]{JP NC,nn} & \opun & \opcf \hyperref[inst:CALL_cc]{CALL NC,nn} & \oplw \hyperref[inst:PUSH_rr]{PUSH DE} & \opab \hyperref[inst:SUB_n]{SUB n} & \opcf \hyperref[inst:RST]{RST 0x10} & \opcf \hyperref[inst:RET_cc]{RET C} & \opcf \hyperref[inst:RETI]{RETI} & \opcf \hyperref[inst:JP_cc]{JP C,nn} & \opun & \opcf \hyperref[inst:CALL_cc]{CALL C,nn} & \opun & \opab \hyperref[inst:SBC_n]{SBC n} & \opcf \hyperref[inst:RST]{RST 0x18} \\ - \hline - Ex & \oplb \hyperref[inst:LDH_n_a]{LDH (n),A} & \oplw \hyperref[inst:POP_rr]{POP HL} & \oplb \hyperref[inst:LDH_c_a]{LDH (C),A} & \opun & \opun & \oplw \hyperref[inst:PUSH_rr]{PUSH HL} & \opab \hyperref[inst:AND_n]{AND n} & \opcf \hyperref[inst:RST]{RST 0x20} & \opaw ADD SP,e & \opcf \hyperref[inst:JP_hl]{JP HL} & \oplb \hyperref[inst:LD_nn_a]{LD (nn),A} & \opun & \opun & \opun & \opab \hyperref[inst:XOR_n]{XOR n} & \opcf \hyperref[inst:RST]{RST 0x28} \\ - \hline - Fx & \oplb \hyperref[inst:LDH_a_n]{LDH A,(n)} & \oplw \hyperref[inst:POP_rr]{POP AF} & \oplb \hyperref[inst:LDH_a_c]{LDH A,(C)} & \opmi \hyperref[inst:DI]{DI} & \opun & \oplw \hyperref[inst:PUSH_rr]{PUSH AF} & \opab \hyperref[inst:OR_n]{OR n} & \opcf \hyperref[inst:RST]{RST 0x30} & \oplw LD HL,SP+e & \oplw \hyperref[inst:LD_sp_hl]{LD SP,HL} & \oplb \hyperref[inst:LD_a_nn]{LD A,(nn)} & \opmi \hyperref[inst:EI]{EI} & \opun & \opun & \opab \hyperref[inst:CP_n]{CP n} & \opcf \hyperref[inst:RST]{RST 0x38} \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - \normalsize - \normalfont\mdseries - \begin{description}[style=sameline] - \item[n] - unsigned 8-bit immediate data - \item[nn] - unsigned 16-bit immediate data - \item[e] - signed 8-bit immediate data - \end{description} - \end{center} -\end{table} - -\begin{table} - \begin{center} - \fontsize{5.8pt}{13pt} - \ttfamily\bfseries - \caption{Sharp SM83 CB-prefixed instructions} - \begin{tabularx}{\linewidth}{|l|C|C|C|C|C|C|C|C|C|C|C|C|C|C|C|C|} - \hline - & x0 & x1 & x2 & x3 & x4 & x5 & x6 & x7 & x8 & x9 & xA & xB & xC & xD & xE & xF \\ - \hline - 0x & \opbi RLC B & \opbi RLC C & \opbi RLC D & \opbi RLC E & \opbi RLC H & \opbi RLC L & \opbi RLC (HL) & \opbi RLC A & \opbi RRC B & \opbi RRC C & \opbi RRC D & \opbi RRC E & \opbi RRC H & \opbi RRC L & \opbi RRC (HL) & \opbi RRC A \\ - \hline - 1x & \opbi RL B & \opbi RL C & \opbi RL D & \opbi RL E & \opbi RL H & \opbi RL L & \opbi RL (HL) & \opbi RL A & \opbi RR B & \opbi RR C & \opbi RR D & \opbi RR E & \opbi RR H & \opbi RR L & \opbi RR (HL) & \opbi RR A \\ - \hline - 2x & \opbi SLA B & \opbi SLA C & \opbi SLA D & \opbi SLA E & \opbi SLA H & \opbi SLA L & \opbi SLA (HL) & \opbi SLA A & \opbi SRA B & \opbi SRA C & \opbi SRA D & \opbi SRA E & \opbi SRA H & \opbi SRA L & \opbi SRA (HL) & \opbi SRA A \\ - \hline - 3x & \opbi SWAP B & \opbi SWAP C & \opbi SWAP D & \opbi SWAP E & \opbi SWAP H & \opbi SWAP L & \opbi SWAP (HL) & \opbi SWAP A & \opbi SRL B & \opbi SRL C & \opbi SRL D & \opbi SRL E & \opbi SRL H & \opbi SRL L & \opbi SRL (HL) & \opbi SRL A \\ - \hline - 4x & \opbi BIT 0,B & \opbi BIT 0,C & \opbi BIT 0,D & \opbi BIT 0,E & \opbi BIT 0,H & \opbi BIT 0,L & \opbi BIT 0,(HL) & \opbi BIT 0,A & \opbi BIT 1,B & \opbi BIT 1,C & \opbi BIT 1,D & \opbi BIT 1,E & \opbi BIT 1,H & \opbi BIT 1,L & \opbi BIT 1,(HL) & \opbi BIT 1,A \\ - \hline - 5x & \opbi BIT 2,B & \opbi BIT 2,C & \opbi BIT 2,D & \opbi BIT 2,E & \opbi BIT 2,H & \opbi BIT 2,L & \opbi BIT 2,(HL) & \opbi BIT 2,A & \opbi BIT 3,B & \opbi BIT 3,C & \opbi BIT 3,D & \opbi BIT 3,E & \opbi BIT 3,H & \opbi BIT 3,L & \opbi BIT 3,(HL) & \opbi BIT 3,A \\ - \hline - 6x & \opbi BIT 4,B & \opbi BIT 4,C & \opbi BIT 4,D & \opbi BIT 4,E & \opbi BIT 4,H & \opbi BIT 4,L & \opbi BIT 4,(HL) & \opbi BIT 4,A & \opbi BIT 5,B & \opbi BIT 5,C & \opbi BIT 5,D & \opbi BIT 5,E & \opbi BIT 5,H & \opbi BIT 5,L & \opbi BIT 5,(HL) & \opbi BIT 5,A \\ - \hline - 7x & \opbi BIT 6,B & \opbi BIT 6,C & \opbi BIT 6,D & \opbi BIT 6,E & \opbi BIT 6,H & \opbi BIT 6,L & \opbi BIT 6,(HL) & \opbi BIT 6,A & \opbi BIT 7,B & \opbi BIT 7,C & \opbi BIT 7,D & \opbi BIT 7,E & \opbi BIT 7,H & \opbi BIT 7,L & \opbi BIT 7,(HL) & \opbi BIT 7,A \\ - \hline - 8x & \opbi RES 0,B & \opbi RES 0,C & \opbi RES 0,D & \opbi RES 0,E & \opbi RES 0,H & \opbi RES 0,L & \opbi RES 0,(HL) & \opbi RES 0,A & \opbi RES 1,B & \opbi RES 1,C & \opbi RES 1,D & \opbi RES 1,E & \opbi RES 1,H & \opbi RES 1,L & \opbi RES 1,(HL) & \opbi RES 1,A \\ - \hline - 9x & \opbi RES 2,B & \opbi RES 2,C & \opbi RES 2,D & \opbi RES 2,E & \opbi RES 2,H & \opbi RES 2,L & \opbi RES 2,(HL) & \opbi RES 2,A & \opbi RES 3,B & \opbi RES 3,C & \opbi RES 3,D & \opbi RES 3,E & \opbi RES 3,H & \opbi RES 3,L & \opbi RES 3,(HL) & \opbi RES 3,A \\ - \hline - Ax & \opbi RES 4,B & \opbi RES 4,C & \opbi RES 4,D & \opbi RES 4,E & \opbi RES 4,H & \opbi RES 4,L & \opbi RES 4,(HL) & \opbi RES 4,A & \opbi RES 5,B & \opbi RES 5,C & \opbi RES 5,D & \opbi RES 5,E & \opbi RES 5,H & \opbi RES 5,L & \opbi RES 5,(HL) & \opbi RES 5,A \\ - \hline - Bx & \opbi RES 6,B & \opbi RES 6,C & \opbi RES 6,D & \opbi RES 6,E & \opbi RES 6,H & \opbi RES 6,L & \opbi RES 6,(HL) & \opbi RES 6,A & \opbi RES 7,B & \opbi RES 7,C & \opbi RES 7,D & \opbi RES 7,E & \opbi RES 7,H & \opbi RES 7,L & \opbi RES 7,(HL) & \opbi RES 7,A \\ - \hline - Cx & \opbi SET 0,B & \opbi SET 0,C & \opbi SET 0,D & \opbi SET 0,E & \opbi SET 0,H & \opbi SET 0,L & \opbi SET 0,(HL) & \opbi SET 0,A & \opbi SET 1,B & \opbi SET 1,C & \opbi SET 1,D & \opbi SET 1,E & \opbi SET 1,H & \opbi SET 1,L & \opbi SET 1,(HL) & \opbi SET 1,A \\ - \hline - Dx & \opbi SET 2,B & \opbi SET 2,C & \opbi SET 2,D & \opbi SET 2,E & \opbi SET 2,H & \opbi SET 2,L & \opbi SET 2,(HL) & \opbi SET 2,A & \opbi SET 3,B & \opbi SET 3,C & \opbi SET 3,D & \opbi SET 3,E & \opbi SET 3,H & \opbi SET 3,L & \opbi SET 3,(HL) & \opbi SET 3,A \\ - \hline - Ex & \opbi SET 4,B & \opbi SET 4,C & \opbi SET 4,D & \opbi SET 4,E & \opbi SET 4,H & \opbi SET 4,L & \opbi SET 4,(HL) & \opbi SET 4,A & \opbi SET 5,B & \opbi SET 5,C & \opbi SET 5,D & \opbi SET 5,E & \opbi SET 5,H & \opbi SET 5,L & \opbi SET 5,(HL) & \opbi SET 5,A \\ - \hline - Fx & \opbi SET 6,B & \opbi SET 6,C & \opbi SET 6,D & \opbi SET 6,E & \opbi SET 6,H & \opbi SET 6,L & \opbi SET 6,(HL) & \opbi SET 6,A & \opbi SET 7,B & \opbi SET 7,C & \opbi SET 7,D & \opbi SET 7,E & \opbi SET 7,H & \opbi SET 7,L & \opbi SET 7,(HL) & \opbi SET 7,A \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - \end{center} -\end{table} - -\end{landscape} - -\end{document} diff --git a/appendix/opcode-tables.typ b/appendix/opcode-tables.typ new file mode 100644 index 0000000..995f6f5 --- /dev/null +++ b/appendix/opcode-tables.typ @@ -0,0 +1,98 @@ +#import "../common.typ": * + +== Instruction set tables + +These tables include all the opcodes in the Sharp SM83 instruction set. The style and layout of these tables was inspired by the opcode tables available at pastraiser.com @pastraiser. + +#set page(flipped: true) + +#let colors = ( + load_8b: rgb("#87CEEB"), + load_16b: rgb("#98FB98"), + alu_8b: rgb("#FFD700"), + alu_16b: rgb("#FFC0CB"), + rotate_shift_bit: rgb("#30D5C8"), + control: rgb("#F4A460"), + misc: rgb("#DB7093"), + undefined: rgb("#C0C0C0"), +) + +#let inset = 5pt +#let border = (content) => box(inset: inset, content) + +#let col_labels = ([], ..range(16).map((x) => border("x" + str(x, base: 16)))) + +#let ops = toml("../opcodes.toml") + +#let opcode_table = (opcodes) => { + monotext(6pt, weight: "bold")[ + #table( + inset: 0pt, + columns: (auto, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr), + align: (col, row) => { + if col == 0 { + right + } else { + center + } + }, + fill: (col, row) => { + if col == 0 or row == 0 { + none + } else { + let opcode = (col - 1) + (row - 1) * 0x10 + if opcodes.len() > opcode { + let op = opcodes.at(opcode) + let category = ops.categories.at(op.category) + colors.at(category.kind) + } + } + }, + ..col_labels, + ..range(16).map((y) => { + ( + border(str(y, base: 16) + "x"), + ..range(16).map((x) => { + let opcode = x + (y * 0x10) + let op = opcodes.at(opcode) + let dest = label("op:" + op.category) + link(dest, block(width: 100%, inset: inset, op.mnemonic)) + }) + ) + }).flatten() + ) + ] +} + +#figure( + opcode_table(ops.opcodes), + caption: "Sharp SM83 instruction set", + kind: table +) + +*Legend:* + +#let color-box = (fill, content) => box(fill: fill, stroke: black, inset: 6pt)[#content] + +#color-box(colors.load_8b)[8-bit loads] +#color-box(colors.load_16b)[16-bit loads] +#color-box(colors.alu_8b)[8-bit arithmetic/logical] +#color-box(colors.alu_16b)[16-bit arithmetic] +#color-box(colors.rotate_shift_bit)[Rotates, shifts, and bit operations] +#color-box(colors.control)[Control flow] +#color-box(colors.misc)[Miscellaneous] +#color-box(colors.undefined)[Undefined] + +#grid( + columns: (auto, 1fr), + gutter: 1em, + [*n*], [unsigned 8-bit immediate data], + [*nn*], [unsigned 16-bit immediate data], + [*e*], [signed 8-bit immediate data], +) + +#figure( + opcode_table(ops.cb_opcodes), + caption: "Sharp SM83 CB-prefixed instructions", + kind: table +) diff --git a/appendix/pinouts.tex b/appendix/pinouts.tex deleted file mode 100644 index d2c04b7..0000000 --- a/appendix/pinouts.tex +++ /dev/null @@ -1,43 +0,0 @@ -%!TEX root = ../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{Chip pinouts} - -\section{CPU chips} - -\begin{figure}[H] - \centering - \includegraphics{DMG-CPU-pinout} - \caption{DMG/SGB CPU (Sharp QFP080-P-1420)} -\end{figure} - -\begin{figure}[H] - \centering - \includegraphics{MGB-CPU-pinout} - \caption{MGB/SGB2 CPU (Sharp QFP080-P-1420)} -\end{figure} - -\section{Cartridge chips} - -\begin{figure}[H] - \centering - \includegraphics{MBC1-pinout} - \caption{MBC1 (Sharp SOP24-P-450) \cite{tauwasser_mbc1}} -\end{figure} - -\begin{figure}[H] - \centering - \includegraphics{MBC2-pinout} - \caption{MBC2 (Sharp SOP28-P-450) \cite{tauwasser_mbc2}} -\end{figure} - -\begin{figure}[H] - \centering - \includegraphics{MBC5-pinout} - \caption{MBC5 (Sharp QFP32-P-0707)} -\end{figure} - -\end{document} diff --git a/appendix/pinouts.typ b/appendix/pinouts.typ new file mode 100644 index 0000000..18f01a7 --- /dev/null +++ b/appendix/pinouts.typ @@ -0,0 +1,34 @@ +== Chip pinouts + +#columns(2)[ + === CPU chips + + #figure( + image("../images/DMG-CPU-pinout.svg", width: 100%), + caption: [DMG/SGB CPU (Sharp QFP080-P-1420)] + ) + + #figure( + image("../images/MGB-CPU-pinout.svg", width: 100%), + caption: [MGB/SGB2 CPU (Sharp QFP080-P-1420)] + ) + + #colbreak() + + === Cartridge chips + + #figure( + image("../images/MBC1-pinout.svg", width: 50%), + caption: [MBC1 (Sharp SOP24-P-450) @tauwasser_mbc1] + ) + + #figure( + image("../images/MBC2-pinout.svg", width: 50%), + caption: [MBC2 (Sharp SOP28-P-450) @tauwasser_mbc2] + ) + + #figure( + image("../images/MBC5-pinout.svg", width: 80%), + caption: [MBC5 (Sharp QFP32-P-0707)] + ) +] diff --git a/chapter/cartridges/huc1.tex b/chapter/cartridges/huc1.tex deleted file mode 100644 index 7599c6f..0000000 --- a/chapter/cartridges/huc1.tex +++ /dev/null @@ -1,14 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{HuC-1 mapper chip} - -HuC-1 supports ROM sizes up to 8 Mbit (64 banks of \hex{4000} bytes), and RAM -sizes up to 256 Kbit (4 banks of \hex{2000} bytes). It also includes a sensor -and a LED for infrared communication. The information in this section is based -on my HuC-1 research. - -\end{document} diff --git a/chapter/cartridges/huc1.typ b/chapter/cartridges/huc1.typ new file mode 100644 index 0000000..adfab1f --- /dev/null +++ b/chapter/cartridges/huc1.typ @@ -0,0 +1,8 @@ +#import "../../common.typ": * + +== HuC-1 mapper chip + +HuC-1 supports ROM sizes up to 8 Mbit (64 banks of #hex("4000") bytes), and RAM +sizes up to 256 Kbit (4 banks of #hex("2000") bytes). It also includes a sensor +and a LED for infrared communication. The information in this section is based +on my HuC-1 research. diff --git a/chapter/cartridges/huc3.tex b/chapter/cartridges/huc3.tex deleted file mode 100644 index d74ce00..0000000 --- a/chapter/cartridges/huc3.tex +++ /dev/null @@ -1,14 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{HuC-3 mapper chip} - -HuC-3 supports ROM sizes up to 16 Mbit (128 banks of \hex{4000} bytes), and RAM -sizes up to 1 Mbit (16 banks of \hex{2000} bytes). Like HuC-1, it includes -support for infrared communication, but also includes a real-time-clock (RTC) -and output pins used to control a piezoelectric buzzer. The information in this -section is based on my HuC-3 research. -\end{document} diff --git a/chapter/cartridges/huc3.typ b/chapter/cartridges/huc3.typ new file mode 100644 index 0000000..f4743d5 --- /dev/null +++ b/chapter/cartridges/huc3.typ @@ -0,0 +1,9 @@ +#import "../../common.typ": * + +== HuC-3 mapper chip + +HuC-3 supports ROM sizes up to 16 Mbit (128 banks of #hex("4000") bytes), and RAM +sizes up to 1 Mbit (16 banks of #hex("2000") bytes). Like HuC-1, it includes +support for infrared communication, but also includes a real-time-clock (RTC) +and output pins used to control a piezoelectric buzzer. The information in this +section is based on my HuC-3 research. diff --git a/chapter/cartridges/mbc1.tex b/chapter/cartridges/mbc1.tex deleted file mode 100644 index 054b2d5..0000000 --- a/chapter/cartridges/mbc1.tex +++ /dev/null @@ -1,455 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{MBC1 mapper chip} - -The majority of games for the original Game Boy use the MBC1 chip. MBC1 -supports ROM sizes up to 16 Mbit (128 banks of \hex{4000} bytes) and RAM -sizes up to 256 Kbit (4 banks of \hex{2000} bytes). The information in -this section is based on my MBC1 research, Tauwasser's research notes -\cite{tauwasser_mbc1}, and Pan Docs \cite{pandocs}. - -\section{MBC1 registers} - -\begin{caveat} - These registers don't have any standard names and are usually referred to - using their address ranges or purposes instead. This document uses names to - clarify which register is meant when referring to one. -\end{caveat} - -The MBC1 chip includes four registers that affect the behaviour of the chip. -Of the cartridge bus address signals, only A13-A15 are connected to the MBC, so -lower address bits don't matter when the CPU is accessing the MBC and all -registers are effectively mapped to address ranges instead of single addresses. -All registers are smaller than 8 bits, and unused bits are simply ignored -during writes. The registers are not directly readable. - -\begin{register}[H] - \caption{\hexrange{0000}{1FFF} - RAMG - MBC1 RAM gate register} - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - U & U & U & U & W-0 & W-0 & W-0 & W-0 \\ - \hline - \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \multicolumn{4}{c|}{RAMG<3:0>} \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } - - \begin{description}[leftmargin=5em, style=nextline] - \item[bit 7-4] - \textbf{Unimplemented}: Ignored during writes - \item[bit 3-0] - \textbf{RAMG<3:0>}: RAM gate register\\ - \bin{1010}= enable access to cartridge RAM\\ - All other values disable access to cartridge RAM - \end{description} -\end{register} - -The RAMG register is used to enable access to the cartridge SRAM if one exists -on the cartridge circuit board. RAM access is disabled by default but can be -enabled by writing to the \hexrange{0000}{1FFF} address range a value with the -bit pattern \bin{1010} in the lower nibble. Upper bits don't matter, but any -other bit pattern in the lower nibble disables access to RAM. - -When RAM access is disabled, all writes to the external RAM area -\hexrange{A000}{BFFF} are ignored, and reads return undefined values. Pan Docs -recommends disabling RAM when it's not being accessed to protect the contents -\cite{pandocs}. - -\begin{speculation} - We don't know the physical implementation of RAMG, but it's certainly - possible that the \bin{1010} bit pattern check is done at write time and the - register actually consists of just a single bit. -\end{speculation} - -\begin{register}[H] - \caption{\hexrange{2000}{3FFF} - BANK1 - MBC1 bank register 1} - - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - U & U & U & W-0 & W-0 & W-0 & W-0 & W-1 \\ - \hline - \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \multicolumn{5}{c|}{BANK1<4:0>} \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } - - \begin{description}[leftmargin=5em, style=nextline] - \item[bit 7-5] - \textbf{Unimplemented}: Ignored during writes - \item[bit 4-0] - \textbf{BANK1<4:0>}: Bank register 1\\ - Never contains the value \bin{00000}.\\ - If \bin{00000} is written, the resulting value will be \bin{00001} instead. - \end{description} -\end{register} - -The 5-bit BANK1 register is used as the lower 5 bits of the ROM bank number -when the CPU accesses the \hexrange{4000}{7FFF} memory area. - -MBC1 doesn't allow the BANK1 register to contain zero (bit pattern -\bin{00000}), so the initial value at reset is \bin{00001} and attempting to -write \bin{00000} will write \bin{00001} instead. This makes it impossible to -read banks \hex{00}, \hex{20}, \hex{40} and \hex{60} from the -\hexrange{4000}{7FFF} memory area, because those bank numbers have \bin{00000} -in the lower bits. Due to the zero value adjustment, requesting any of these -banks actually requests the next bank (e.g. \hex{21} instead of \hex{20}). - -\begin{register}[H] - \caption{\hexrange{4000}{5FFF} - BANK2 - MBC1 bank register 2} - - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - U & U & U & U & U & U & W-0 & W-0 \\ - \hline - \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \multicolumn{2}{c|}{BANK2<1:0>} \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } - - \begin{description}[leftmargin=5em, style=nextline] - \item[bit 7-2] - \textbf{Unimplemented}: Ignored during writes - \item[bit 1-0] - \textbf{BANK2<1:0>}: Bank register 2 - \end{description} -\end{register} - -The 2-bit BANK2 register can be used as the upper bits of the ROM bank number, -or as the 2-bit RAM bank number. Unlike BANK1, BANK2 doesn't disallow zero, so -all 2-bit values are possible. - -\begin{register} - \caption{\hexrange{6000}{7FFF} - MODE - MBC1 mode register} - - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - U & U & U & U & U & U & U & W-0 \\ - \hline - \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & MODE \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } - - \begin{description}[leftmargin=5em, style=nextline] - \item[bit 7-1] - \textbf{Unimplemented}: Ignored during writes - \item[bit 0] - \textbf{MODE}: Mode register\\ - \bin{1}= BANK2 affects accesses to \hexrange{0000}{3FFF}, \hexrange{4000}{7FFF}, \hexrange{A000}{BFFF}\\ - \bin{0}= BANK2 affects only accesses to \hexrange{4000}{7FFF} - \end{description} -\end{register} - -The MODE register determines how the BANK2 register value is used during memory -accesses. - -\begin{warning} - Most documentation, including Pan Docs \cite{pandocs}, calls value \bin{0} - ROM banking mode, and value \bin{1} RAM banking mode. This terminology - reflects the common use cases, but "RAM banking" is slightly misleading because - value \bin{1} also affects ROM reads in multicart cartridges and cartridges - that have a 8 or 16 Mbit ROM chip. -\end{warning} - -\section{ROM in the \hexrange{0000}{7FFF} area} - -In MBC1 cartridges, the A0-A13 cartridge bus signals are connected directly to -the corresponding ROM pins, and the remaining ROM pins (A14-A20) are controlled -by the MBC1. These remaining pins form the ROM bank number. - -When the \hexrange{0000}{3FFF} address range is accessed, the effective bank -number depends on the MODE register. In MODE \bin{0} the bank number is always -0, but in MODE \bin{1} it's formed by shifting the BANK2 register value left by -5 bits. - -When the \hexrange{4000}{7FFF} addess range is accessed, the effective bank -number is always a combination of BANK1 and BANK2 register values. - -If the cartridge ROM is smaller than 16 Mbit, there are less ROM address pins -to connect to and therefore some bank number bits are ignored. For example, 4 -Mbit ROMs only need a 5-bit bank number, so the BANK2 register value is always -ignored because those bits are simply not connected to the ROM. - -\begin{table}[H] - \caption{Mapping of physical ROM address bits in MBC1 carts} - \centering - \begin{tabular}{|l|c|c|c|} - \hline - & \multicolumn{3}{c|}{ROM address bits} \\ - Accessed address & \multicolumn{2}{c|}{Bank number} & Address within bank \\ - \hline - & 20-19 & 18-14 & 13-0 \\ - \hline - \hexrange{0000}{3FFF}, MODE = \bin{0} & \bin{00} & \bin{00000} & A<13:0> \\ - \hline - \hexrange{0000}{3FFF}, MODE = \bin{1} & BANK2 & \bin{00000} & A<13:0> \\ - \hline - \hexrange{4000}{7FFF} & BANK2 & BANK1 & A<13:0> \\ - \hline - \end{tabular} -\end{table} - -\subsection{ROM banking example 1} - -Let's assume we have previously written \hex{12} to the BANK1 register and -\bin{01} to the BANK2 register. The effective bank number during ROM reads -depends on which address range we read and on the value of the MODE register: - -\begin{description}[style=nextline] - \item[Value of the BANK1 register] - { - \ttfamily - 0b\colorbox{blue!30}{10010} - } - \item[Value of the BANK2 register] - { - \ttfamily - 0b\colorbox{red!30}{01} - } - \item[Effective ROM bank number (reading \hexrange{4000}{7FFF})] - { - \ttfamily - 0b\colorbox{red!30}{01}\colorbox{blue!30}{10010} (= 50 = \hex{32}) - } - \item[Effective ROM bank number (reading \hexrange{0000}{3FFF}, MODE = \bin{0})] - { - \ttfamily - 0b\colorbox{gray!10}{00}\colorbox{gray!10}{00000} (= 0 = \hex{00}) - } - \item[Effective ROM bank number (reading \hexrange{0000}{3FFF}, MODE = \bin{1})] - { - \ttfamily - 0b\colorbox{red!30}{01}\colorbox{gray!10}{00000} (= 32 = \hex{20}) - } -\end{description} - -\subsection{ROM banking example 2} - -Let's assume we have previously requested ROM bank number 68, MBC1 mode is -\bin{0}, and we are now reading a byte from \hex{72A7}. The actual -physical ROM address that will be read is going to be \hex{1132A7} and is -constructed in the following way: - -\begin{description}[leftmargin=15em,style=nextline] - \item[Value of the BANK1 register] - { - \ttfamily - 0b\colorbox{blue!30}{00100} - } - \item[Value of the BANK2 register] - { - \ttfamily - 0b\colorbox{red!30}{10} - } - \item[ROM bank number] - { - \ttfamily - 0b\colorbox{red!30}{10}\colorbox{blue!30}{00100} (= 68 = \hex{44}) - } - \item[Address being read] - { - \ttfamily - 0b\colorbox{gray!10}{01}\colorbox{green!30}{11 0010 1010 0111} (= \hex{72A7}) - } - \item[Actual physical ROM address] - { - \ttfamily - 0b\colorbox{red!30}{1 0}\colorbox{blue!30}{001 00}\colorbox{green!30}{11 0010 1010 0111} (= \hex{1132A7}) - } -\end{description} - -\section{RAM in the \hexrange{A000}{BFFF} area} - -Some MBC1 carts include SRAM, which is mapped to the \hexrange{A000}{BFFF} -area. If no RAM is present, or RAM is not enabled with the RAMG register, all -reads return undefined values and writes have no effect. - -On boards that have RAM, the A0-A12 cartridge bus signals are connected -directly to the corresponding RAM pins, and pins A13-A14 are controlled by the -MBC1. Most of the time the RAM size is 64 Kbit, which corresponds to a single -bank of \hex{2000} bytes. With larger RAM sizes the BANK2 register value can be -used for RAM banking to provide the two high address bits. - -In MODE \bin{0} the BANK2 register value is not used, so the first RAM bank is -always mapped to the \hexrange{A000}{BFFF} area. In MODE \bin{1} the BANK2 -register value is used as the bank number. - -\begin{table}[H] - \caption{Mapping of physical RAM address bits in MBC1 carts} - \centering - \begin{tabular}{|l|c|c|} - \hline - & \multicolumn{2}{c|}{RAM address bits} \\ - Accessed address & Bank number & Address within bank \\ - \hline - & 14-13 & 12-0 \\ - \hline - \hexrange{A000}{BFFF}, MODE = \bin{0} & \bin{00} & A<12:0> \\ - \hline - \hexrange{A000}{BFFF}, MODE = \bin{1} & BANK2 & A<12:0> \\ - \hline - \end{tabular} -\end{table} - -\subsection{RAM banking example 1} - -Let's assume we have previously written \bin{10} to the BANK2 register, MODE is -\bin{1}, RAMG is \bin{1010} and we are now reading a byte from \hex{B123}. The -actual physical RAM address that will be read is going to be \hex{5123} and is -constructed in the following way: - -\begin{description}[leftmargin=15em,style=nextline] - \item[Value of the BANK2 register] - { - \ttfamily - 0b\colorbox{red!30}{10} - } - \item[Address being read] - { - \ttfamily - 0b\colorbox{gray!10}{101}\colorbox{green!30}{1 0001 0010 0011} (= \hex{B123}) - } - \item[Actual physical RAM address] - { - \ttfamily - 0b\colorbox{red!30}{10}\colorbox{green!30}{1 0001 0010 0011} (= \hex{5123}) - } -\end{description} - -\section{MBC1 multicarts ("MBC1M")} - -MBC1 is also used in a couple of "multicart" cartridges, which include more -than one game on the same cartridge. These cartridges use the same regular MBC1 -chip, but the circuit board is wired a bit differently. This alternative wiring -is sometimes called "MBC1M", but technically the mapper chip is the same. All -known MBC1 multicarts use 8 Mbit ROMs, so there's no definitive wiring for -other ROM sizes. - -In MBC1 multicarts bit 4 of the BANK1 register is not physically connected to -anything, so it's skipped. This means that the bank number is actually a 6-bit -number. In all known MBC1 multicarts the games reserve 16 banks each, so BANK2 -can actually be considered "game number", while BANK1 is the internal bank -number within the selected game. At reset BANK2 is \bin{00}, and the "game" in -this slot is actually a game selection menu. The menu code selects MODE \bin{1} -and writes the game number to BANK2 once the user selects a game. - -From a ROM banking point of view, multicarts simply skip bit 4 of the BANK1 -register, but otherwise the behaviour is the same. MODE \bin{1} guarantees that -all ROM accesses, including accesses to \hexrange{0000}{3FFF}, use the BANK2 -register value. - -\begin{table}[H] - \caption{Mapping of physical ROM address bits in MBC1 multicarts} - \centering - \begin{tabular}{|l|c|c|c|} - \hline - & \multicolumn{3}{c|}{ROM address bits} \\ - Accessed address & \multicolumn{2}{c|}{Bank number} & Address within bank \\ - \hline - & 19-18 & 17-14 & 13-0 \\ - \hline - \hexrange{0000}{3FFF}, MODE = \bin{0} & \bin{00} & \bin{0000} & A<13:0> \\ - \hline - \hexrange{0000}{3FFF}, MODE = \bin{1} & BANK2 & \bin{0000} & A<13:0> \\ - \hline - \hexrange{4000}{7FFF} & BANK2 & BANK1<3:0> & A<13:0> \\ - \hline - \end{tabular} -\end{table} - -\subsection{ROM banking example 1} - -Let's assume we have previously requested "game number" 3 (= \bin{11}) -and ROM bank number 29 (= \hex{1D}), MBC1 mode is \bin{1}, and we are -now reading a byte from \hex{6C15}. The actual physical ROM address that -will be read is going to be \hex{F6C15} and is constructed in the -following way: - -\begin{description}[leftmargin=15em,style=nextline] - \item[Value of the BANK1 register] - { - \ttfamily - 0b\colorbox{gray!10}{1}\colorbox{blue!30}{1101} - } - \item[Value of the BANK2 register] - { - \ttfamily - 0b\colorbox{red!30}{11} - } - \item[ROM bank number] - { - \ttfamily - 0b\colorbox{red!30}{11}\colorbox{blue!30}{1101} (= 61 = \hex{3D}) - } - \item[Address being read] - { - \ttfamily - 0b\colorbox{gray!10}{01}\colorbox{green!30}{10 1100 0001 0101} (= \hex{6C15}) - } - \item[Actual physical ROM address] - { - \ttfamily - 0b\colorbox{red!30}{11}\colorbox{blue!30}{11 01}\colorbox{green!30}{10 1100 0001 0101} (= \hex{F6C15}) - } -\end{description} - -\subsection{Detecting multicarts} - -MBC1 multicarts are not detectable by simply looking at the ROM header, because -the ROM type value is just one of the normal MBC1 values. However, detection is -possible by going through BANK2 values and looking at "bank 0" of each -multicart game and doing some heuristics based on the header data. All the -included games, including the game selection menu, have proper header data. -One example of a good heuristic is logo data verification. - -So, if you have a 8 Mbit cart with MBC1, first assume that it's a multicart and -bank numbers are 6-bit values. Set BANK1 to zero and loop through the four -possible BANK2 values while checking the data at \hexrange{0104}{0133}. In -other words, check logo data starting from physical ROM locations \hex{00104}, -\hex{40104}, \hex{80104}, and \hex{C0104}. If proper logo data exists with most -of the BANK2 values, the cart is most likely a multicart. Note that multicarts -can just have two actual games, so one of the locations might not have the -header data in place. - -\section{Dumping MBC1 carts} - -MBC1 cartridge dumping is fairly straightforward with the right hardware. The -total number of banks is read from the header, and each bank is read one byte -at a time. However, BANK1 register zero-adjustment and multicart cartridges -need to be considered in ROM dumping code. - -Banks \hex{20}, \hex{40} and \hex{60} can only be read from the -\hexrange{0000}{3FFF} memory area and only when MODE register value is \bin{1}. -Using MODE \bin{1} has no undesirable effects when doing ROM dumping, so using -it at all times is recommended for simplicity. - -Multicarts should be detected using the logo check described earlier, and if a -multicart is detected, BANK1 should be considered a 4-bit register in the -dumping code. - -\begin{listing}[H] - \inputminted[frame=lines]{python}{code-snippets/mbc1_rom_dump.py} - \caption{Python pseudo-code for MBC1 ROM dumping} -\end{listing} - -\end{document} diff --git a/chapter/cartridges/mbc1.typ b/chapter/cartridges/mbc1.typ new file mode 100644 index 0000000..e764c41 --- /dev/null +++ b/chapter/cartridges/mbc1.typ @@ -0,0 +1,331 @@ +#import "../../common.typ": * + +== MBC1 mapper chip + +The majority of games for the original Game Boy use the MBC1 chip. MBC1 supports ROM sizes up to 16 Mbit (128 banks of #hex("4000") bytes) and RAM sizes up to 256 Kbit (4 banks of #hex("2000") bytes). The information in this section is based on my MBC1 research, Tauwasser's research notes @tauwasser_mbc1, and Pan Docs @pandocs. + +=== MBC1 registers + +#caveat[ + These registers don't have any standard names and are usually referred to using their address ranges or purposes instead. This document uses names to clarify which register is meant when referring to one. +] + +The MBC1 chip includes four registers that affect the behaviour of the chip. Of the cartridge bus address signals, only A13-A15 are connected to the MBC, so lower address bits don't matter when the CPU is accessing the MBC and all registers are effectively mapped to address ranges instead of single addresses. All registers are smaller than 8 bits, and unused bits are simply ignored during writes. The registers are not directly readable. + +#reg-figure( + caption: [#hex-range("0000", "1FFF") - RAMG - MBC1 RAM gate register] +)[ + #reg-table( + [U], [U], [U], [U], [W-0], [W-0], [W-0], [W-0], + unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), colspanx(4)[RAMG\<3:0\>], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) + #set align(left) + #grid( + columns: (auto, 1fr), + gutter: 1em, + [*bit 7-4*], [*Unimplemented*: Ignored during writes], + [*bit 3-0*], [ + *RAMG\<3:0\>*: RAM gate register\ + #bin("1010") = enable access to chip RAM\ + All other values disable access to chip RAM + ] + ) +] + +The RAMG register is used to enable access to the cartridge SRAM if one exists on the cartridge circuit board. RAM access is disabled by default but can be enabled by writing to the #hex-range("0000", "1FFF") address range a value with the bit pattern #bin("1010") in the lower nibble. Upper bits don't matter, but any other bit pattern in the lower nibble disables access to RAM. + +When RAM access is disabled, all writes to the external RAM area #hex-range("A000", "BFFF") are ignored, and reads return undefined values. Pan Docs recommends disabling RAM when it's not being accessed to protect the contents @pandocs. + +#speculation[ + We don't know the physical implementation of RAMG, but it's certainly possible that the #bin("1010") bit pattern check is done at write time and the register actually consists of just a single bit. +] + +#reg-figure( + caption: [#hex-range("2000", "3FFF") - BANK1 - MBC1 bank register 1] +)[ + #reg-table( + [U], [U], [U], [W-0], [W-0], [W-0], [W-0], [W-1], + unimpl-bit(), unimpl-bit(), unimpl-bit(), colspanx(5)[BANK1\<4:0\>], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) + #set align(left) + #grid( + columns: (auto, 1fr), + gutter: 1em, + [*bit 7-5*], [*Unimplemented*: Ignored during writes], + [*bit 4-0*], [ + *BANK1\<4:0\>*: Bank register 1\ + Never contains the value #bin("00000").\ + If #bin("00000") is written, the resulting value will be #bin("00001") instead. + ] + ) +] + +The 5-bit BANK1 register is used as the lower 5 bits of the ROM bank number when the CPU accesses the #hex-range("4000", "7FFF") memory area. + +MBC1 doesn't allow the BANK1 register to contain zero (bit pattern #bin("00000")), so the initial value at reset is #bin("00001") and attempting to write #bin("00000") will write #bin("00001") instead. This makes it impossible to read banks #hex("00"), #hex("20"), #hex("40") and #hex("60") from the #hex-range("4000", "7FFF") memory area, because those bank numbers have #bin("00000") in the lower bits. Due to the zero value adjustment, requesting any of these banks actually requests the next bank (e.g. #hex("21") instead of #hex("20")). + +#reg-figure( + caption: [#hex-range("2000", "3FFF") - BANK1 - MBC1 bank register 2] +)[ + #reg-table( + [U], [U], [U], [U], [U], [U], [W-0], [W-0], + unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), colspanx(2)[BANK2\<1:0\>], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) + #set align(left) + #grid( + columns: (auto, 1fr), + gutter: 1em, + [*bit 7-2*], [*Unimplemented*: Ignored during writes], + [*bit 1-0*], [ + *BANK2\<1:0\>*: Bank register 2 + ] + ) +] + +The 2-bit BANK2 register can be used as the upper bits of the ROM bank number, or as the 2-bit RAM bank number. Unlike BANK1, BANK2 doesn't disallow zero, so all 2-bit values are possible. + +#reg-figure( + caption: [#hex-range("6000", "7FFF") - MODE - MBC1 mode register] +)[ + #reg-table( + [U], [U], [U], [U], [U], [U], [U], [W-0], + unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), [MODE], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) + #set align(left) + #grid( + columns: (auto, 1fr), + gutter: 1em, + [*bit 7-1*], [*Unimplemented*: Ignored during writes], + [*bit 0*], [ + *MODE*: Mode register\ + #bin("1") = BANK2 affects accesses to #hex-range("0000", "3FFF"), #hex-range("4000", "7FFF"), #hex-range("A000", "BFFF")\ + #bin("0") = BANK2 affects only accesses to #hex-range("4000", "7FFF") + ] + ) +] + +The MODE register determines how the BANK2 register value is used during memory accesses. + +#warning[ + Most documentation, including Pan Docs @pandocs, calls value #bin("0") ROM banking mode, and value #bin("1") RAM banking mode. This terminology reflects the common use cases, but "RAM banking" is slightly misleading because value #bin("1") also affects ROM reads in multicart cartridges and cartridges that have a 8 or 16 Mbit ROM chip. +] + +=== ROM in the #hex-range("0000", "7FFF") area + +In MBC1 cartridges, the A0-A13 cartridge bus signals are connected directly to the corresponding ROM pins, and the remaining ROM pins (A14-A20) are controlled by the MBC1. These remaining pins form the ROM bank number. + +When the #hex-range("0000", "3FFF") address range is accessed, the effective bank number depends on the MODE register. In MODE #bin("0") the bank number is always 0, but in MODE #bin("1") it's formed by shifting the BANK2 register value left by 5 bits. + +When the #hex-range("4000", "7FFF") addess range is accessed, the effective bank number is always a combination of BANK1 and BANK2 register values. + +If the cartridge ROM is smaller than 16 Mbit, there are less ROM address pins to connect to and therefore some bank number bits are ignored. For example, 4 Mbit ROMs only need a 5-bit bank number, so the BANK2 register value is always ignored because those bits are simply not connected to the ROM. + +#figure( + tablex( + columns: 4, + auto-hlines: false, + align: center, + hlinex(), + [], colspanx(3)[ROM address bits], + [Accessed address], colspanx(2)[Bank number], [Address within bank], + hlinex(), + [], [20-19], [18-14], [13-0], + hlinex(), + [#hex-range("0000", "3FFF"), MODE = #bin("0")], bin("00"), bin("00000"), [A\<13:0\>], + hlinex(), + [#hex-range("0000", "3FFF"), MODE = #bin("1")], [BANK2], bin("00000"), [A\<13:0\>], + hlinex(), + hex-range("4000", "7FFF"), [BANK2], [BANK1], [A\<13:0\>], + hlinex(), + ), + kind: table, + caption: "Mapping of physical ROM address bits in MBC1 carts" +) + +==== ROM banking example 1 + +Let's assume we have previously written #hex("12") to the BANK1 register and #bin("01") to the BANK2 register. The effective bank number during ROM reads depends on which address range we read and on the value of the MODE register: + +#block(breakable: false, { + let bank1 = box(inset: (y: 2pt), fill: rgb("#0000ff4c"))[10010] + let bank2 = box(inset: (y: 2pt), fill: rgb("#ff00004c"))[01] + let prefix = box(inset: (y: 2pt))[0b] + let pass(text) = box(inset: (y: 2pt), fill: rgb("#00000019"), text) + tablex( + columns: 3, + align: (left + horizon, right + horizon, left + horizon), + auto-vlines: false, + auto-hlines: false, + [*Value of the BANK 1 register*], + monotext[#prefix#bank1], [], + [*Value of the BANK 2 register*], + monotext[#prefix#bank2], [], + [*Effective ROM bank number\ (reading #hex-range("4000", "7FFF"))*], + monotext[#prefix#bank2#bank1], [(= 50 = #hex("32"))], + [*Effective ROM bank number\ (reading #hex-range("0000", "3FFF"), MODE = #bin("0"))*], + monotext[#prefix#pass[00]#pass[00000]], [(= 0 = #hex("00"))], + [*Effective ROM bank number\ (reading #hex-range("0000", "3FFF"), MODE = #bin("1"))*], + monotext[#prefix#bank2#pass[00000]], [(= 32 = #hex("20"))], + ) +}) + +==== ROM banking example 2 + +Let's assume we have previously requested ROM bank number 68, MBC1 mode is #bin("0"), and we are now reading a byte from #hex("72A7"). The actual physical ROM address that will be read is going to be #hex("1132A7") and is constructed in the following way: + +#block(breakable: false, { + let bank1(content) = box(inset: (y: 2pt), fill: rgb("#0000ff4c"), content) + let bank2(content) = box(inset: (y: 2pt), fill: rgb("#ff00004c"), content) + let addr(content) = box(inset: (y: 2pt), fill: rgb("#00ff004c"), content) + let prefix = box(inset: (y: 2pt))[0b] + let pass(content) = box(inset: (y: 2pt), fill: rgb("#00000019"), content) + tablex( + columns: 3, + align: (left + horizon, right + horizon, left + horizon), + auto-vlines: false, + auto-hlines: false, + [*Value of the BANK 1 register*], + monotext[#prefix#bank1("00100")], [], + [*Value of the BANK 2 register*], + monotext[#prefix#bank2("10")], [], + [*ROM bank number*], + monotext[#prefix#bank2("10")#bank1("00100")], [(= 68 = #hex("44"))], + [*Address being read*], + monotext[#prefix#pass[01]#addr("11 0010 1010 0111")], [(= #hex("72A7"))], + [*Actual physical ROM address*], + monotext[#prefix#bank2("1 0")#bank1("001 00")#addr("11 0010 1010 0111")], [(= #hex("1132A7"))], + ) +}) + +=== RAM in the #hex-range("A000", "BFFF") area + +Some MBC1 carts include SRAM, which is mapped to the #hex-range("A000", "BFFF") area. If no RAM is present, or RAM is not enabled with the RAMG register, all reads return undefined values and writes have no effect. + +On boards that have RAM, the A0-A12 cartridge bus signals are connected directly to the corresponding RAM pins, and pins A13-A14 are controlled by the MBC1. Most of the time the RAM size is 64 Kbit, which corresponds to a single bank of #hex("2000") bytes. With larger RAM sizes the BANK2 register value can be used for RAM banking to provide the two high address bits. + +In MODE #bin("0") the BANK2 register value is not used, so the first RAM bank is always mapped to the #hex-range("A000", "BFFF") area. In MODE #bin("1") the BANK2 register value is used as the bank number. + +#figure( + tablex( + columns: 3, + auto-hlines: false, + align: center + bottom, + hlinex(), + [], colspanx(2)[RAM address bits], + [Accessed address], [Bank number], [Address within bank], + hlinex(), + [], [14-13], [12-0], + hlinex(), + [#hex-range("A000", "BFFF"), MODE = #bin("0")], bin("00"), [A\<12:0\>], + hlinex(), + [#hex-range("A000", "BFFF"), MODE = #bin("1")], [BANK2], [A\<12:0\>], + hlinex(), + ), + kind: table, + caption: "Mapping of physical RAM address bits in MBC1 carts" +) + +==== RAM banking example 1 + +Let's assume we have previously written #bin("10") to the BANK2 register, MODE is #bin("1"), RAMG is #bin("1010") and we are now reading a byte from #hex("B123"). The actual physical RAM address that will be read is going to be #hex("5123") and is constructed in the following way: + +#block(breakable: false, { + let bank2(content) = box(inset: (y: 2pt), fill: rgb("#ff00004c"), content) + let addr(content) = box(inset: (y: 2pt), fill: rgb("#00ff004c"), content) + let prefix = box(inset: (y: 2pt))[0b] + let pass(content) = box(inset: (y: 2pt), fill: rgb("#00000019"), content) + tablex( + columns: 3, + align: (left + horizon, right + horizon, left + horizon), + auto-vlines: false, + auto-hlines: false, + [*Value of the BANK 2 register*], + monotext[#prefix#bank2("10")], [], + [*Address being read*], + monotext[#prefix#pass[101]#addr("1 0001 0010 0011")], [(= #hex("B123"))], + [*Actual physical RAM address*], + monotext[#prefix#bank2("10")#addr("1 0001 0010 0011")], [(= #hex("5123"))], + ) +}) + +=== MBC1 multicarts ("MBC1M") + +MBC1 is also used in a couple of "multicart" cartridges, which include more than one game on the same cartridge. These cartridges use the same regular MBC1 chip, but the circuit board is wired a bit differently. This alternative wiring is sometimes called "MBC1M", but technically the mapper chip is the same. All known MBC1 multicarts use 8 Mbit ROMs, so there's no definitive wiring for other ROM sizes. + +In MBC1 multicarts bit 4 of the BANK1 register is not physically connected to anything, so it's skipped. This means that the bank number is actually a 6-bit number. In all known MBC1 multicarts the games reserve 16 banks each, so BANK2 can actually be considered "game number", while BANK1 is the internal bank number within the selected game. At reset BANK2 is #bin("00"), and the "game" in this slot is actually a game selection menu. The menu code selects MODE #bin("1") and writes the game number to BANK2 once the user selects a game. + +From a ROM banking point of view, multicarts simply skip bit 4 of the BANK1 register, but otherwise the behaviour is the same. MODE #bin("1") guarantees that all ROM accesses, including accesses to #hex-range("0000", "3FFF"), use the BANK2 register value. + +#figure( + tablex( + columns: 4, + auto-hlines: false, + align: center, + hlinex(), + [], colspanx(3)[ROM address bits], + [Accessed address], colspanx(2)[Bank number], [Address within bank], + hlinex(), + [], [19-18], [17-14], [13-0], + hlinex(), + [#hex-range("0000", "3FFF"), MODE = #bin("0")], bin("00"), bin("0000"), [A\<13:0\>], + hlinex(), + [#hex-range("0000", "3FFF"), MODE = #bin("1")], [BANK2], bin("0000"), [A\<13:0\>], + hlinex(), + hex-range("4000", "7FFF"), [BANK2], [BANK1\<3:0\>], [A\<13:0\>], + hlinex(), + ), + kind: table, + caption: "Mapping of physical ROM address bits in MBC1 multicarts" +) + +==== ROM banking example 1 + +Let's assume we have previously requested "game number" 3 (= #bin("11")) and ROM bank number 29 (= #hex("1D")), MBC1 mode is #bin("1"), and we are now reading a byte from #hex("6C15"). The actual physical ROM address that will be read is going to be #hex("F6C15") and is constructed in the following way: + +#block(breakable: false, { + let bank1(content) = box(inset: (y: 2pt), fill: rgb("#0000ff4c"), content) + let bank2(content) = box(inset: (y: 2pt), fill: rgb("#ff00004c"), content) + let addr(content) = box(inset: (y: 2pt), fill: rgb("#00ff004c"), content) + let prefix = box(inset: (y: 2pt))[0b] + let pass(content) = box(inset: (y: 2pt), fill: rgb("#00000019"), content) + tablex( + columns: 3, + align: (left + horizon, right + horizon, left + horizon), + auto-vlines: false, + auto-hlines: false, + [*Value of the BANK 1 register*], + monotext[#prefix#pass("1")#bank1("1101")], [], + [*Value of the BANK 2 register*], + monotext[#prefix#bank2("11")], [], + [*ROM bank number*], + monotext[#prefix#bank2("11")#bank1("1101")], [(= 61 = #hex("3D"))], + [*Address being read*], + monotext[#prefix#pass[01]#addr("10 1100 0001 0101")], [(= #hex("6C15"))], + [*Actual physical ROM address*], + monotext[#prefix#bank2("11")#bank1("11 01")#addr("10 1100 0001 0101")], [(= #hex("F6C15"))], + ) +}) + +==== Detecting multicarts + +MBC1 multicarts are not detectable by simply looking at the ROM header, because the ROM type value is just one of the normal MBC1 values. However, detection is possible by going through BANK2 values and looking at "bank 0" of each multicart game and doing some heuristics based on the header data. All the included games, including the game selection menu, have proper header data. One example of a good heuristic is logo data verification. + +So, if you have a 8 Mbit cart with MBC1, first assume that it's a multicart and bank numbers are 6-bit values. Set BANK1 to zero and loop through the four possible BANK2 values while checking the data at #hex-range("0104", "0133"). In other words, check logo data starting from physical ROM locations #hex("00104"), #hex("40104"), #hex("80104"), and #hex("C0104"). If proper logo data exists with most of the BANK2 values, the cart is most likely a multicart. Note that multicarts can just have two actual games, so one of the locations might not have the header data in place. + + +=== Dumping MBC1 carts + +MBC1 cartridge dumping is fairly straightforward with the right hardware. The total number of banks is read from the header, and each bank is read one byte at a time. However, BANK1 register zero-adjustment and multicart cartridges need to be considered in ROM dumping code. + +Banks #hex("20"), #hex("40") and #hex("60") can only be read from the #hex-range("0000", "3FFF") memory area and only when MODE register value is #bin("1"). Using MODE #bin("1") has no undesirable effects when doing ROM dumping, so using it at all times is recommended for simplicity. + +Multicarts should be detected using the logo check described earlier, and if a multicart is detected, BANK1 should be considered a 4-bit register in the dumping code. + +#figure( + raw(read("../../code-snippets/mbc1_rom_dump.py"), lang: "python", block: true), + caption: "Python pseudo-code for MBC1 ROM dumping" +) diff --git a/chapter/cartridges/mbc2.tex b/chapter/cartridges/mbc2.tex deleted file mode 100644 index 1a0f197..0000000 --- a/chapter/cartridges/mbc2.tex +++ /dev/null @@ -1,186 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{MBC2 mapper chip} - -MBC2 supports ROM sizes up to 2 Mbit (16 banks of \hex{4000} bytes) and -includes an internal 512x4 bit RAM array, which is its unique feature. The -information in this section is based on my MBC2 research, Tauwasser's research -notes \cite{tauwasser_mbc2}, and Pan Docs \cite{pandocs}. - -\begin{speculation} - MBC1 is strictly more powerful than MBC2 because it supports more ROM and - RAM. This raises a very important question: why does MBC2 exist? It's - possible that Nintendo tried to integrate a small amount of RAM on the MBC - chip for cost reasons, but it seems that this didn't work out very well since - all later MBCs revert this design decision and use separate RAM chips. -\end{speculation} - -\section{MBC2 registers} - -\begin{caveat} - These registers don't have any standard names and are usually referred to - using one of their addresses or purposes instead. This document uses names to - clarify which register is meant when referring to one. -\end{caveat} - -The MBC2 chip includes two registers that affect the behaviour of the chip. The -registers are mapped a bit differently compared to other MBCs. Both registers -are accessible within \hexrange{0000}{3FFF}, and within that range, the -register is chosen based on the A8 address signal. In practice, this means that -the registers are mapped to memory in an alternating pattern. For example, -\hex{0000}, \hex{2000} and \hex{3000} are RAMG, and \hex{0100}, \hex{2100} and -\hex{3100} are ROMB. Both registers are smaller than 8 bits, and unused bits -are simply ignored during writes. The registers are not directly readable. - -\begin{register}[H] - \caption{\hexrange{0000}{3FFF} when A8=\bin{0} - RAMG - MBC2 RAM gate register} - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - U & U & U & U & W-0 & W-0 & W-0 & W-0 \\ - \hline - \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \multicolumn{4}{c|}{RAMG<3:0>} \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } - - \begin{description}[leftmargin=5em, style=nextline] - \item[bit 7-4] - \textbf{Unimplemented}: Ignored during writes - \item[bit 3-0] - \textbf{RAMG<3:0>}: RAM gate register\\ - \bin{1010}= enable access to chip RAM\\ - All other values disable access to chip RAM - \end{description} -\end{register} - -The 4-bit MBC2 RAMG register works in a similar manner as MBC1 RAMG, so the -upper bits don't matter and only the bit pattern \bin{1010} enables access to -RAM. - -When RAM access is disabled, all writes to the external RAM area -\hexrange{A000}{BFFF} are ignored, and reads return undefined values. Pan Docs -recommends disabling RAM when it's not being accessed to protect the contents -\cite{pandocs}. - -\begin{speculation} - We don't know the physical implementation of RAMG, but it's certainly - possible that the \bin{1010} bit pattern check is done at write time and the - register actually consists of just a single bit. -\end{speculation} - -\begin{register}[H] - \caption{\hexrange{0000}{3FFF} when A8=\bin{1} - ROMB - MBC2 ROM bank register} - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - U & U & U & U & W-0 & W-0 & W-0 & W-1 \\ - \hline - \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \multicolumn{4}{c|}{ROMB<3:0>} \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } - - \begin{description}[leftmargin=5em, style=nextline] - \item[bit 7-4] - \textbf{Unimplemented}: Ignored during writes - \item[bit 3-0] - \textbf{ROMB<3:0>}: ROM bank register\\ - Never contains the value \bin{0000}.\\ - If \bin{0000} is written, the resulting value will be \bin{0001} instead. - \end{description} -\end{register} - -The 4-bit ROMB register is used as the ROM bank number when the CPU accesses -the \hexrange{4000}{7FFF} memory area. - -Like MBC1 BANK1, the MBC2 ROMB register doesn't allow zero (bit pattern -\bin{0000}) in the register, so any attempt to write \bin{0000} writes -\bin{0001} instead. - -\section{ROM in the \hexrange{0000}{7FFF} area} - -In MBC2 cartridges, the A0-A13 cartridge bus signals are connected directly to -the corresponding ROM pins, and the remaining ROM pins (A14-A17) are controlled -by the MBC2. These remaining pins form the ROM bank number. - -When the \hexrange{0000}{3FFF} address range is accessed, the effective bank -number is always 0. - -When the \hexrange{4000}{7FFF} address range is accessed, the effective bank -number is the current ROMB register value. - -\begin{table}[H] - \caption{Mapping of physical ROM address bits in MBC2 carts} - \centering - \begin{tabular}{|l|c|c|} - \hline - & \multicolumn{2}{c|}{ROM address bits} \\ - Accessed address & Bank number & Address within bank \\ - \hline - & 17-14 & 13-0 \\ - \hline - \hexrange{0000}{3FFF} & \bin{0000} & A<13:0> \\ - \hline - \hexrange{4000}{7FFF} & ROMB & A<13:0> \\ - \hline - \end{tabular} -\end{table} - -\section{RAM in the \hexrange{A000}{BFFF} area} - -All MBC2 carts include SRAM, because it is located directly inside the MBC2 -chip. These cartridges never use a separate RAM chip, but battery backup -circuitry and a battery are optional. If RAM is not enabled with the RAMG -register, all reads return undefined values and writes have no effect. - -MBC2 RAM is only 4-bit RAM, so the upper 4 bits of data do not physically exist -in the chip. When writing to it, the upper 4 bits are ignored. When reading -from it, the upper 4 data signals are not driven by the chip, so their content -is undefined and should not be relied on. - -MBC2 RAM consists of 512 addresses, so only A0-A8 matter when accessing the RAM -region. There is no banking, and the \hexrange{A000}{BFFF} area is larger than -the RAM, so the addresses wrap around. For example, accessing \hex{A000} is the -same as accessing \hex{A200}, so it is possible to write to the former address -and later read the written data using the latter address. - -\begin{table}[H] - \caption{Mapping of physical RAM address bits in MBC2 carts} - \centering - \begin{tabular}{|l|c|} - \hline - & RAM address bits \\ - Accessed address & \\ - \hline - & 8-0 \\ - \hline - \hexrange{A000}{BFFF} & A<8:0> \\ - \hline - \end{tabular} -\end{table} - -\section{Dumping MBC2 carts} - -MBC2 cartridges are very simple to dump. The total number of banks is read from -the header, and each bank is read one byte at a time. ROMB zero adjustment must -be considered in the ROM dumping code, but this only means that bank 0 should -be read from \hexrange{0000}{3FFF} and not from \hexrange{4000}{7FFF} like -other banks. - -\begin{listing}[H] - \inputminted[frame=lines]{python}{code-snippets/mbc2_rom_dump.py} - \caption{Python pseudo-code for MBC2 ROM dumping} -\end{listing} - -\end{document} diff --git a/chapter/cartridges/mbc2.typ b/chapter/cartridges/mbc2.typ new file mode 100644 index 0000000..d88ac7c --- /dev/null +++ b/chapter/cartridges/mbc2.typ @@ -0,0 +1,133 @@ +#import "../../common.typ": * + +== MBC2 mapper chip + +MBC2 supports ROM sizes up to 2 Mbit (16 banks of #hex("4000") bytes) and includes an internal 512x4 bit RAM array, which is its unique feature. The information in this section is based on my MBC2 research, Tauwasser's research notes @tauwasser_mbc2, and Pan Docs @pandocs. + +#speculation[ + MBC1 is strictly more powerful than MBC2 because it supports more ROM and RAM. This raises a very important question: why does MBC2 exist? It's possible that Nintendo tried to integrate a small amount of RAM on the MBC chip for cost reasons, but it seems that this didn't work out very well since all later MBCs revert this design decision and use separate RAM chips. +] + +=== MBC2 registers + +#caveat[ + These registers don't have any standard names and are usually referred to using one of their addresses or purposes instead. This document uses names to clarify which register is meant when referring to one. +] + +The MBC2 chip includes two registers that affect the behaviour of the chip. The registers are mapped a bit differently compared to other MBCs. Both registers are accessible within #hex-range("0000", "3FFF"), and within that range, the register is chosen based on the A8 address signal. In practice, this means that the registers are mapped to memory in an alternating pattern. For example, #hex("0000"), #hex("2000") and #hex("3000") are RAMG, and #hex("0100"), #hex("2100") and #hex("3100") are ROMB. Both registers are smaller than 8 bits, and unused bits are simply ignored during writes. The registers are not directly readable. + +#reg-figure( + caption: [#hex-range("0000", "3FFF") when A8=#bin("0") - RAMG - MBC2 RAM gate register] +)[ + #reg-table( + [U], [U], [U], [U], [W-0], [W-0], [W-0], [W-0], + unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), colspanx(4)[RAMG\<3:0\>], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) + #set align(left) + #grid( + columns: (auto, 1fr), + gutter: 1em, + [*bit 7-4*], [*Unimplemented*: Ignored during writes], + [*bit 3-0*], [ + *RAMG\<3:0\>*: RAM gate register\ + #bin("1010") = enable access to chip RAM\ + All other values disable access to chip RAM + ] + ) +] + +The 4-bit MBC2 RAMG register works in a similar manner as MBC1 RAMG, so the upper bits don't matter and only the bit pattern #bin("1010") enables access to RAM. + +When RAM access is disabled, all writes to the external RAM area #hex-range("A000", "BFFF") are ignored, and reads return undefined values. Pan Docs recommends disabling RAM when it's not being accessed to protect the contents @pandocs. + +#speculation[ + We don't know the physical implementation of RAMG, but it's certainly possible that the #bin("1010") bit pattern check is done at write time and the register actually consists of just a single bit. +] + +#reg-figure( + caption: [#hex-range("0000", "3FFF") when A8=#bin("1") - ROMB - MBC2 ROM bank register] +)[ + #reg-table( + [U], [U], [U], [U], [W-0], [W-0], [W-0], [W-1], + unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), colspanx(4)[ROMB\<3:0\>], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) + #set align(left) + #grid( + columns: (auto, 1fr), + gutter: 1em, + [*bit 3-0*], [ + *ROMB\<3:0\>*: ROM bank register\ + Never contains the value #bin("0000").\ + If #bin("0000") is written, the resulting value will be #bin("0001") instead. + ] + ) +] + +The 4-bit ROMB register is used as the ROM bank number when the CPU accesses the #hex-range("4000", "7FFF") memory area. + +Like MBC1 BANK1, the MBC2 ROMB register doesn't allow zero (bit pattern #bin("0000")) in the register, so any attempt to write #bin("0000") writes #bin("0001") instead. + +=== ROM in the #hex-range("0000", "7FFF") area + +In MBC2 cartridges, the A0-A13 cartridge bus signals are connected directly to the corresponding ROM pins, and the remaining ROM pins (A14-A17) are controlled by the MBC2. These remaining pins form the ROM bank number. + +When the #hex-range("0000", "3FFF") address range is accessed, the effective bank number is always 0. + +When the #hex-range("4000", "7FFF") address range is accessed, the effective bank number is the current ROMB register value. + +#figure( + tablex( + columns: 3, + auto-hlines: false, + align: center, + hlinex(), + [], colspanx(2)[ROM address bits], + [Accessed address], [Bank number], [Address within bank], + hlinex(), + [], [17-14], [13-0], + hlinex(), + hex-range("0000", "3FFF"), bin("0000"), [A\<13:0\>], + hlinex(), + hex-range("4000", "7FFF"), [ROMB], [A\<13:0\>], + hlinex(), + ), + kind: table, + caption: "Mapping of physical ROM address bits in MBC2 carts" +) + +=== RAM in the #hex-range("A000", "BFFF") area + +All MBC2 carts include SRAM, because it is located directly inside the MBC2 chip. These cartridges never use a separate RAM chip, but battery backup circuitry and a battery are optional. If RAM is not enabled with the RAMG register, all reads return undefined values and writes have no effect. + +MBC2 RAM is only 4-bit RAM, so the upper 4 bits of data do not physically exist in the chip. When writing to it, the upper 4 bits are ignored. When reading from it, the upper 4 data signals are not driven by the chip, so their content is undefined and should not be relied on. + +MBC2 RAM consists of 512 addresses, so only A0-A8 matter when accessing the RAM region. There is no banking, and the #hex-range("A000", "BFFF") area is larger than the RAM, so the addresses wrap around. For example, accessing #hex("A000") is the same as accessing #hex("A200"), so it is possible to write to the former address and later read the written data using the latter address. + +#figure( + tablex( + columns: 2, + auto-hlines: false, + align: center + bottom, + hlinex(), + [], [RAM address bits], + [Accessed address], [], + hlinex(), + [], [8-0], + hlinex(), + hex-range("A000", "BFFF"), [A\<8:0\>], + hlinex(), + ), + kind: table, + caption: "Mapping of physical RAM address bits in MBC2 carts" +) + +=== Dumping MBC2 carts + +MBC2 cartridges are very simple to dump. The total number of banks is read from the header, and each bank is read one byte at a time. ROMB zero adjustment must be considered in the ROM dumping code, but this only means that bank 0 should be read from #hex-range("0000", "3FFF") and not from #hex-range("4000", "7FFF") like other banks. + +#figure( + raw(read("../../code-snippets/mbc2_rom_dump.py"), lang: "python", block: true), + caption: "Python pseudo-code for MBC2 ROM dumping" +) diff --git a/chapter/cartridges/mbc3.tex b/chapter/cartridges/mbc3.tex deleted file mode 100644 index b4af261..0000000 --- a/chapter/cartridges/mbc3.tex +++ /dev/null @@ -1,15 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{MBC3 mapper chip} - -MBC3 supports ROM sizes up to 16 Mbit (128 banks of \hex{4000} bytes), and RAM -sizes up to 256 Kbit (4 banks of \hex{2000} bytes). It also includes a -real-time clock (RTC) that can be clocked with a quartz crystal on the -cartridge even when the Game Boy is powered down. The information in this -section is based on my MBC3 research, and Pan Docs \cite{pandocs}. - -\end{document} diff --git a/chapter/cartridges/mbc3.typ b/chapter/cartridges/mbc3.typ new file mode 100644 index 0000000..0006747 --- /dev/null +++ b/chapter/cartridges/mbc3.typ @@ -0,0 +1,5 @@ +#import "../../common.typ": * + +== MBC3 mapper chip + +MBC3 supports ROM sizes up to 16 Mbit (128 banks of #hex("4000") bytes), and RAM sizes up to 256 Kbit (4 banks of #hex("2000") bytes). It also includes a real-time clock (RTC) that can be clocked with a quartz crystal on the cartridge even when the Game Boy is powered down. The information in this section is based on my MBC3 research, and Pan Docs @pandocs. diff --git a/chapter/cartridges/mbc30.tex b/chapter/cartridges/mbc30.tex deleted file mode 100644 index 12fa6e5..0000000 --- a/chapter/cartridges/mbc30.tex +++ /dev/null @@ -1,23 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{MBC30 mapper chip} - -MBC30 is a variant of MBC3 used by Japanese Pokemon Crystal to support a larger -ROM chip and a larger RAM chip. Featurewise MBC30 is almost identical to MBC3, -but supports ROM sizes up to 32 Mbit (256 banks of \hex{4000} bytes), and RAM -sizes up to 512 Kbit (8 banks of \hex{2000} bytes). Information in this -section is based on my MBC30 research. - -\begin{warning} - The circuit board of Japanese Pokemon Crystal includes a 1 Mbit RAM chip, but - MBC30 is limited to 512 Kbit RAM. One of the RAM address pins is unused, so - half of the RAM is wasted and is inaccessible without modifications. So, the - game only uses 512 Kbit and there is a mismatch between accessible and the - physical amounts of RAM. -\end{warning} - -\end{document} diff --git a/chapter/cartridges/mbc30.typ b/chapter/cartridges/mbc30.typ new file mode 100644 index 0000000..dc8f5af --- /dev/null +++ b/chapter/cartridges/mbc30.typ @@ -0,0 +1,9 @@ +#import "../../common.typ": * + +== MBC30 mapper chip + +MBC30 is a variant of MBC3 used by Japanese Pokemon Crystal to support a larger ROM chip and a larger RAM chip. Featurewise MBC30 is almost identical to MBC3, but supports ROM sizes up to 32 Mbit (256 banks of #hex("4000") bytes), and RAM sizes up to 512 Kbit (8 banks of #hex("2000") bytes). Information in this section is based on my MBC30 research. + +#warning[ + The circuit board of Japanese Pokemon Crystal includes a 1 Mbit RAM chip, but MBC30 is limited to 512 Kbit RAM. One of the RAM address pins is unused, so half of the RAM is wasted and is inaccessible without modifications. So, the game only uses 512 Kbit and there is a mismatch between accessible and the physical amounts of RAM. +] diff --git a/chapter/cartridges/mbc5.tex b/chapter/cartridges/mbc5.tex deleted file mode 100644 index 5720708..0000000 --- a/chapter/cartridges/mbc5.tex +++ /dev/null @@ -1,134 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{MBC5 mapper chip} - -The majority of games for Game Boy Color use the MBC5 chip. MBC5 supports ROM -sizes up to 64 Mbit (512 banks of \hex{4000} bytes), and RAM sizes up to 1 Mbit -(16 banks of \hex{2000} bytes). The information in this section is based on my -MBC5 research, and The Cycle-Accurate Game Boy Docs \cite{tcagbd}. - -\section{MBC5 registers} - -\begin{register}[H] - \caption{\hexrange{0000}{1FFF} - RAMG - MBC5 RAM gate register} - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - W-0 & W-0 & W-0 & W-0 & W-0 & W-0 & W-0 & W-0 \\ - \hline - \multicolumn{8}{|c|}{RAMG<7:0>} \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } - - \begin{description}[leftmargin=5em, style=nextline] - \item[bit 7-0] - \textbf{RAMG<7:0>}: RAM gate register\\ - \bin{00001010}= enable access to cartridge RAM\\ - All other values disable access to cartridge RAM - \end{description} -\end{register} - -The 8-bit MBC5 RAMG register works in a similar manner as MBC1 RAMG, but it is -a full 8-bit register so upper bits matter when writing to it. Only -\bin{00001010} enables RAM access, and all other values (including -\bin{10001010} for example) disable access to RAM. - -When RAM access is disabled, all writes to the external RAM area -\hexrange{A000}{BFFF} are ignored, and reads return undefined values. Pan Docs -recommends disabling RAM when it's not being accessed to protect the contents -\cite{pandocs}. - -\begin{speculation} - We don't know the physical implementation of RAMG, but it's certainly - possible that the \bin{00001010} bit pattern check is done at write time and the - register actually consists of just a single bit. -\end{speculation} - -\begin{register}[H] - \caption{\hexrange{2000}{2FFF} - ROMB0 - MBC5 lower ROM bank register} - - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - W-0 & W-0 & W-0 & W-0 & W-0 & W-0 & W-0 & W-1 \\ - \hline - \multicolumn{8}{|c|}{ROMB0<7:0>} \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } - - \begin{description}[leftmargin=5em, style=nextline] - \item[bit 7-0] - \textbf{ROMB0<7:0>}: Lower ROM bank register - \end{description} -\end{register} - -The 8-bit ROMB0 register is used as the lower 8 bits of the ROM bank number -when the CPU accesses the \hexrange{4000}{7FFF} memory area. - -\begin{register}[H] - \caption{\hexrange{3000}{3FFF} - ROMB1 - MBC5 upper ROM bank register} - - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - U & U & U & U & U & U & U & W-0 \\ - \hline - \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & ROMB1 \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } - - \begin{description}[leftmargin=5em, style=nextline] - \item[bit 7-1] - \textbf{Unimplemented}: Ignored during writes - \item[bit 0] - \textbf{ROMB1}: Upper ROM bank register - \end{description} -\end{register} - -The 1-bit ROMB1 register is used as the most significant bit (bit 9) of the ROM -bank number when the CPU accesses the \hexrange{4000}{7FFF} memory area. - -\begin{register}[H] - \caption{\hexrange{4000}{5FFF} - RAMB - MBC5 RAM bank register} - - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - U & U & U & U & W-0 & W-0 & W-0 & W-0 \\ - \hline - \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \cellcolor{LightGray} & \multicolumn{4}{c|}{RAMB<3:0>} \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } - - \begin{description}[leftmargin=5em, style=nextline] - \item[bit 7-4] - \textbf{Unimplemented}: Ignored during writes - \item[bit 3-0] - \textbf{RAMB<3:0>}: RAM bank register - \end{description} -\end{register} - -The 4-bit RAMB register is used as the RAM bank number when the CPU accesses -the \hexrange{A000}{BFFF} memory area. - -\end{document} diff --git a/chapter/cartridges/mbc5.typ b/chapter/cartridges/mbc5.typ new file mode 100644 index 0000000..e6b6761 --- /dev/null +++ b/chapter/cartridges/mbc5.typ @@ -0,0 +1,93 @@ +#import "../../common.typ": * + +== MBC5 mapper chip + +The majority of games for Game Boy Color use the MBC5 chip. MBC5 supports ROM sizes up to 64 Mbit (512 banks of #hex("4000") bytes), and RAM sizes up to 1 Mbit (16 banks of #hex("2000") bytes). The information in this section is based on my MBC5 research, and The Cycle-Accurate Game Boy Docs @tcagbd. + +=== MBC5 registers + +#reg-figure( + caption: [#hex-range("0000", "1FFF") - RAMG - MBC5 RAM gate register] +)[ + #reg-table( + [W-0], [W-0], [W-0], [W-0], [W-0], [W-0], [W-0], [W-0], + colspanx(8)[RAMG\<7:0\>], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) + #set align(left) + #grid( + columns: (auto, 1fr), + gutter: 1em, + [*bit 7-0*], [ + *RAMG\<7:0\>*: RAM gate register\ + #bin("00001010") = enable access to cartridge RAM\ + All other values disable access to cartridge RAM + ] + ) +] + +The 8-bit MBC5 RAMG register works in a similar manner as MBC1 RAMG, but it is a full 8-bit register so upper bits matter when writing to it. Only #bin("00001010") enables RAM access, and all other values (including #bin("10001010") for example) disable access to RAM. + +When RAM access is disabled, all writes to the external RAM area #hex-range("A000", "BFFF") are ignored, and reads return undefined values. Pan Docs recommends disabling RAM when it's not being accessed to protect the contents @pandocs. + +#speculation[ + We don't know the physical implementation of RAMG, but it's certainly possible that the #bin("00001010") bit pattern check is done at write time and the register actually consists of just a single bit. +] + +#reg-figure( + caption: [#hex-range("2000", "2FFF") - ROMB0 - MBC5 lower ROM bank register] +)[ + #reg-table( + [W-0], [W-0], [W-0], [W-0], [W-0], [W-0], [W-0], [W-1], + colspanx(8)[ROMB0\<7:0\>], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) + #set align(left) + #grid( + columns: (auto, 1fr), + gutter: 1em, + [*bit 7-0*], [ + *ROMB0\<7:0\>*: Lower ROM bank register + ] + ) +] + +The 8-bit ROMB0 register is used as the lower 8 bits of the ROM bank number when the CPU accesses the #hex-range("4000", "7FFF") memory area. + +#reg-figure( + caption: [#hex-range("3000", "3FFF") - ROMB1 - MBC5 upper ROM bank register] +)[ + #reg-table( + [U], [U], [U], [U], [U], [U], [U], [W-0], + unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), [ROMB1], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) + #set align(left) + #grid( + columns: (auto, 1fr), + gutter: 1em, + [*bit 7-1*], [*Unimplemented*: Ignored during writes], + [*bit 0*], [*ROMB1*: Upper ROM bank register], + ) +] + +The 1-bit ROMB1 register is used as the most significant bit (bit 9) of the ROM bank number when the CPU accesses the #hex-range("4000", "7FFF") memory area. + +#reg-figure( + caption: [#hex-range("4000", "5FFF") - RAMB - MBC5 RAM bank register] +)[ + #reg-table( + [U], [U], [U], [U], [W-0], [W-0], [W-0], [W-0], + unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), colspanx(4)[RAMB\<3:0\>], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) + #set align(left) + #grid( + columns: (auto, 1fr), + gutter: 1em, + [*bit 7-4*], [*Unimplemented*: Ignored during writes], + [*bit 3-0*], [*RAMB\<3:0\>*: RAM bank register], + ) +] + +The 4-bit RAMB register is used as the RAM bank number when the CPU accesses the #hex-range("A000", "BFFF") memory area. diff --git a/chapter/cartridges/mbc6.tex b/chapter/cartridges/mbc6.tex deleted file mode 100644 index 6f29a8c..0000000 --- a/chapter/cartridges/mbc6.tex +++ /dev/null @@ -1,13 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{MBC6 mapper chip} - -MBC6 supports ROM sizes up to 16 Mbit (256 banks of \hex{2000} bytes), and RAM -sizes up to 4 Mbit (128 banks of \hex{1000} bytes). The information in this -section is based on my MBC6 research. - -\end{document} diff --git a/chapter/cartridges/mbc6.typ b/chapter/cartridges/mbc6.typ new file mode 100644 index 0000000..63f2e58 --- /dev/null +++ b/chapter/cartridges/mbc6.typ @@ -0,0 +1,5 @@ +#import "../../common.typ": * + +== MBC6 mapper chip + +MBC6 supports ROM sizes up to 16 Mbit (256 banks of #hex("2000") bytes), and RAM sizes up to 4 Mbit (128 banks of #hex("1000") bytes). The information in this section is based on my MBC6 research. diff --git a/chapter/cartridges/mbc7.tex b/chapter/cartridges/mbc7.tex deleted file mode 100644 index a542009..0000000 --- a/chapter/cartridges/mbc7.tex +++ /dev/null @@ -1,11 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{MBC7} - -TODO. - -\end{document} diff --git a/chapter/cartridges/mbc7.typ b/chapter/cartridges/mbc7.typ new file mode 100644 index 0000000..cfd3e8b --- /dev/null +++ b/chapter/cartridges/mbc7.typ @@ -0,0 +1,5 @@ +#import "../../common.typ": * + +== MBC7 + +TODO. diff --git a/chapter/cartridges/mmm01.tex b/chapter/cartridges/mmm01.tex deleted file mode 100644 index a245a3b..0000000 --- a/chapter/cartridges/mmm01.tex +++ /dev/null @@ -1,11 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{MMM01} - -TODO. - -\end{document} diff --git a/chapter/cartridges/mmm01.typ b/chapter/cartridges/mmm01.typ new file mode 100644 index 0000000..de581e0 --- /dev/null +++ b/chapter/cartridges/mmm01.typ @@ -0,0 +1,5 @@ +#import "../../common.typ": * + +== MMM01 + +TODO. diff --git a/chapter/cartridges/tama5.tex b/chapter/cartridges/tama5.tex deleted file mode 100644 index 325636c..0000000 --- a/chapter/cartridges/tama5.tex +++ /dev/null @@ -1,11 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{TAMA5} - -TODO. - -\end{document} diff --git a/chapter/cartridges/tama5.typ b/chapter/cartridges/tama5.typ new file mode 100644 index 0000000..5985e5c --- /dev/null +++ b/chapter/cartridges/tama5.typ @@ -0,0 +1,5 @@ +#import "../../common.typ": * + +== TAMA5 + +TODO. diff --git a/chapter/cpu/instruction-set.tex b/chapter/cpu/instruction-set.tex deleted file mode 100644 index 209b08e..0000000 --- a/chapter/cpu/instruction-set.tex +++ /dev/null @@ -1,2351 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{Sharp SM83 instruction set} - -\section{8-bit load instructions} - -8-bit load instructions transfer one byte of data between two 8-bit registers, -or between one 8-bit register and location in memory. - -\subsection{LD r, r': Load register (register)} -\label{inst:LD_r_r} - -Load to the 8-bit register \texttt{r}, data from the 8-bit register \texttt{r'}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{01xxxyyy}/various - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{LD r, r'} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: LD B, C -if opcode == 0x41: - B = C -\end{verbatim} -\end{description} - -\subsection{LD r, n: Load register (immediate)} -\label{inst:LD_r_n} - -Load to the 8-bit register \texttt{r}, the immediate data \texttt{n}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00xxx110}/various + \texttt{n} - \item[Length] - 2 byte - \item[Duration] - 2 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{LD r, n} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: n} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1} ; [opacity=0.4] 8D{PC+2} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: LD B, n -if opcode == 0x06: - B = read(PC++) -\end{verbatim} -\end{description} - -\subsection{LD r, (HL): Load register (indirect HL)} -\label{inst:LD_r_hl} - -Load to the 8-bit register \texttt{r}, data from the absolute address specified by the 16-bit register HL. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{01xxx110}/various - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{LD r, (HL)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: data} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{HL} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: LD B, (HL) -if opcode == 0x46: - B = read(HL) -\end{verbatim} -\end{description} - -\subsection{LD (HL), r: Load from register (indirect HL)} -\label{inst:LD_hl_r} - -Load to the absolute address specified by the 16-bit register HL, data from the 8-bit register \texttt{r}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{01110xxx}/various - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{LD (HL), r} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{W: data} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{HL} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: LD (HL), B -if opcode == 0x70: - write(HL, B) -\end{verbatim} -\end{description} - -\subsection{LD (HL), n: Load from immediate data (indirect HL)} -\label{inst:LD_hl_n} - -Load to the absolute address specified by the 16-bit register HL, the immediate data \texttt{n}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00110110}/\hex{36} + \texttt{n} - \item[Length] - 2 bytes - \item[Duration] - 3 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 24D{LD (HL), n} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: n} 8D{W: n} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1} 8D{HL} ; [opacity=0.4] 8D{PC+2} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x36: - n = read(PC++) - write(HL, n) -\end{verbatim} -\end{description} - -\subsection{LD A, (BC): Load accumulator (indirect BC)} -\label{inst:LD_a_bc} - -Load to the 8-bit A register, data from the absolute address specified by the 16-bit register BC. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00001010}/\hex{0A} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{LD A, (BC)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: data} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{BC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x0A: - A = read(BC) -\end{verbatim} -\end{description} - -\subsection{LD A, (DE): Load accumulator (indirect DE)} -\label{inst:LD_a_de} - -Load to the 8-bit A register, data from the absolute address specified by the 16-bit register DE. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00011010}/\hex{1A} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{LD A, (DE)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: data} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{DE} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x1A: - A = read(DE) -\end{verbatim} -\end{description} - -\subsection{LD (BC), A: Load from accumulator (indirect BC)} -\label{inst:LD_bc_a} - -Load to the absolute address specified by the 16-bit register BC, data from the 8-bit A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00000010}/\hex{02} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{LD (BC), A} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{W: data} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{BC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x02: - write(BC, A) -\end{verbatim} -\end{description} - -\subsection{LD (DE), A: Load from accumulator (indirect DE)} -\label{inst:LD_de_a} - -Load to the absolute address specified by the 16-bit register DE, data from the 8-bit A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00010010}/\hex{12} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{LD (DE), A} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{W: data} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{DE} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x12: - write(DE, A) -\end{verbatim} -\end{description} - -\subsection{LD A, (nn): Load accumulator (direct)} -\label{inst:LD_a_nn} - -Load to the 8-bit A register, data from the absolute address specified by the 16-bit operand \texttt{nn}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11111010}/\hex{FA} + LSB of \texttt{nn} + MSB of \texttt{nn} - \item[Length] - 3 bytes - \item[Duration] - 4 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4} 8D{M5/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 32D{LD A, (nn)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: lsb(nn)} 8D{R: msb(nn)} 8D{R: data} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1} 8D{PC+2} 8D{nn} ; [opacity=0.4] 8D{PC+3} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xFA: - nn = unsigned_16(lsb=read(PC++), msb=read(PC++)) - A = read(nn) -\end{verbatim} -\end{description} - -\subsection{LD (nn), A: Load from accumulator (direct)} -\label{inst:LD_nn_a} - -Load to the absolute address specified by the 16-bit operand \texttt{nn}, data from the 8-bit A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11101010}/\hex{EA} + LSB of \texttt{nn} + MSB of \texttt{nn} - \item[Length] - 3 bytes - \item[Duration] - 4 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4} 8D{M5/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 32D{LD (nn), A} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: lsb(nn)} 8D{R: msb(nn)} 8D{W: data} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1} 8D{PC+2} 8D{nn} ; [opacity=0.4] 8D{PC+3} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xEA: - nn = unsigned_16(lsb=read(PC++), msb=read(PC++)) - write(nn, A) -\end{verbatim} -\end{description} - -\subsection{LDH A, (C): Load accumulator (indirect \hex{FF00}+C)} -\label{inst:LDH_a_c} - -Load to the 8-bit A register, data from the address specified by the 8-bit C -register. The full 16-bit absolute address is obtained by setting the most -significant byte to \hex{FF} and the least significant byte to the value of C, -so the possible range is \hexrange{FF00}{FFFF}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11110010}/\hex{F2} - \item[Length] - 1 bytes - \item[Duration] - 2 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{LDH A, (C)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: A} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{\hex{FF00}+C} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xF2: - A = read(unsigned_16(lsb=C, msb=0xFF)) -\end{verbatim} -\end{description} - -\subsection{LDH (C), A: Load from accumulator (indirect \hex{FF00}+C)} -\label{inst:LDH_c_a} - -Load to the address specified by the 8-bit C register, data from the 8-bit A -register. The full 16-bit absolute address is obtained by setting the most -significant byte to \hex{FF} and the least significant byte to the value of C, -so the possible range is \hexrange{FF00}{FFFF}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11100010}/\hex{E2} - \item[Length] - 1 bytes - \item[Duration] - 2 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{LDH (C), A} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{W: A} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{\hex{FF00}+C} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xE2: - write(unsigned_16(lsb=C, msb=0xFF), A) -\end{verbatim} -\end{description} - -\subsection{LDH A, (n): Load accumulator (direct \hex{FF00}+n)} -\label{inst:LDH_a_n} - -Load to the 8-bit A register, data from the address specified by the 8-bit -immediate data \texttt{n}. The full 16-bit absolute address is obtained by -setting the most significant byte to \hex{FF} and the least significant byte to -the value of \texttt{n}, so the possible range is \hexrange{FF00}{FFFF}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11110000}/\hex{F0} - \item[Length] - 2 bytes - \item[Duration] - 3 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 24D{LD A, (C)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: n} 8D{R: A} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1} 8D{\hex{FF00}+n} ; [opacity=0.4] 8D{PC+2} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xF0: - n = read(PC++) - A = read(unsigned_16(lsb=n, msb=0xFF)) -\end{verbatim} -\end{description} - -\subsection{LDH (n), A: Load from accumulator (direct \hex{FF00}+n)} -\label{inst:LDH_n_a} - -Load to the address specified by the 8-bit immediate data \texttt{n}, data from -the 8-bit A register. The full 16-bit absolute address is obtained by setting -the most significant byte to \hex{FF} and the least significant byte to the -value of \texttt{n}, so the possible range is \hexrange{FF00}{FFFF}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11100000}/\hex{E0} - \item[Length] - 2 bytes - \item[Duration] - 3 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 24D{LDH (n), A} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: n} 8D{W: A} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1} 8D{\hex{FF00}+n} ; [opacity=0.4] 8D{PC+2} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xE0: - n = read(PC++) - write(unsigned_16(lsb=n, msb=0xFF), A) -\end{verbatim} -\end{description} - -\subsection{LD A, (HL-): Load accumulator (indirect HL, decrement)} -\label{inst:LD_a_hld} - -Load to the 8-bit A register, data from the absolute address specified by the -16-bit register HL. The value of HL is decremented after the memory read. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00111010}/\hex{3A} - \item[Length] - 1 bytes - \item[Duration] - 2 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{LD A, (HL-)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: A} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{HL} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x3A: - A = read(HL--) -\end{verbatim} -\end{description} - -\subsection{LD (HL-), A: Load from accumulator (indirect HL, decrement)} -\label{inst:LD_hld_a} - -Load to the absolute address specified by the 16-bit register HL, data from the -8-bit A register. The value of HL is decremented after the memory write. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00110010}/\hex{32} - \item[Length] - 1 bytes - \item[Duration] - 2 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{LD (HL-), A} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{W: A} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{HL} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x32: - write(HL--, A) -\end{verbatim} -\end{description} - -\subsection{LD A, (HL+): Load accumulator (indirect HL, increment)} -\label{inst:LD_a_hli} - -Load to the 8-bit A register, data from the absolute address specified by the -16-bit register HL. The value of HL is incremented after the memory read. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00101010}/\hex{2A} - \item[Length] - 1 bytes - \item[Duration] - 2 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{LD A, (HL+)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: A} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{HL} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x2A: - A = read(HL++) -\end{verbatim} -\end{description} - -\subsection{LD (HL+), A: Load from accumulator (indirect HL, increment)} -\label{inst:LD_hli_a} - -Load to the absolute address specified by the 16-bit register HL, data from the -8-bit A register. The value of HL is incremented after the memory write. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00100010}/\hex{22} - \item[Length] - 1 bytes - \item[Duration] - 2 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{LD (HL+), A} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{W: A} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{HL} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x22: - write(HL++, A) -\end{verbatim} -\end{description} - -\section{16-bit load instructions} - -16-bit load instructions transfer two bytes of data between two 16-bit -registers, or between one 16-bit register and two sequential locations in -memory. - -\subsection{LD rr, nn: Load 16-bit register / register pair} -\label{inst:LD_rr_nn} - -Load to the 16-bit register \texttt{rr}, the immediate 16-bit data \texttt{nn}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00xx0001}/various + LSB of \texttt{nn} + MSB of \texttt{nn} - \item[Length] - 3 byte - \item[Duration] - 3 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 24D{LD rr, nn} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: lsb(nn)} 8D{R: msb(nn)} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1} 8D{PC+2} ; [opacity=0.4] 8D{PC+3} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: LD BC, nn -if opcode == 0x01: - nn = unsigned_16(lsb=read(PC++), msb=read(PC++)) - BC = nn -\end{verbatim} -\end{description} - -\subsection{LD (nn), SP: Load from stack pointer (direct)} -\label{inst:LD_nn_sp} - -Load to the absolute address specified by the 16-bit operand \texttt{nn}, data from the 16-bit SP register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00001000}/\hex{08} + LSB of \texttt{nn} + MSB of \texttt{nn} - \item[Length] - 3 byte - \item[Duration] - 5 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4} 8D{M5} 8D{M6/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 40D{LD (nn), SP} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: lsb(nn)} 8D{R: msb(nn)} 8D{W: lsb(SP)} 8D{W: msb(SP)} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1} 8D{PC+2} 8D{nn} 8D{nn+1} ; [opacity=0.4] 8D{PC+3} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x08: - nn = unsigned_16(lsb=read(PC++), msb=read(PC++)) - write(nn, lsb(SP)) - write(nn+1, msb(SP)) -\end{verbatim} -\end{description} - -\subsection{LD SP, HL: Load stack pointer from HL} -\label{inst:LD_sp_hl} - -Load to the 16-bit SP register, data from the 16-bit HL register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11111001}/\hex{F9} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{LD SP, HL} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8U ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8U ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xF9: - SP = HL -\end{verbatim} -\end{description} - -\subsection{PUSH rr: Push to stack} -\label{inst:PUSH_rr} - -Push to the stack memory, data from the 16-bit register \texttt{rr}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11xx0101}/various - \item[Length] - 1 byte - \item[Duration] - 4 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4} 8D{M5/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 32D{PUSH rr} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8U 8D{W: msb(rr)} 8D{W: lsb(rr)} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{SP} 8D{SP-1} 8D{SP-2} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: PUSH BC -if opcode == 0xC5: - SP-- - write(SP--, msb(BC)) - write(SP, lsb(BC)) -\end{verbatim} -\end{description} - -\subsection{POP rr: Pop from stack} -\label{inst:POP_rr} - -Pops to the 16-bit register \texttt{rr}, data from the stack memory. - -This instruction does not do calculations that affect flags, but POP AF -completely replaces the F register value, so all flags are changed based on the -8-bit data that is read from memory. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11xx0001}/various - \item[Length] - 1 byte - \item[Duration] - 3 machine cycles - \item[Flags] - see the instruction description - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 24D{POP rr} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: lsb(rr)} 8D{R: msb(rr)} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{SP} 8D{SP+1} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: POP BC -if opcode == 0xC1: - BC = unsigned_16(lsb=read(SP++), msb=read(SP++)) -\end{verbatim} -\end{description} - -\section{8-bit arithmetic and logical instructions} - -\subsection{ADD r: Add (register)} -\label{inst:ADD_r} - -Adds to the 8-bit A register, the 8-bit register \texttt{r}, and stores the -result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{10000xxx}/various - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - Z = \faStar, N = 0, H = \faStar, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{ADD r} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: ADD B -if opcode == 0x80: - result, carry_per_bit = A + B - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 1 if carry_per_bit[3] else 0 - flags.C = 1 if carry_per_bit[7] else 0 -\end{verbatim} -\end{description} - -\subsection{ADD (HL): Add (indirect HL)} -\label{inst:ADD_hl} - -Adds to the 8-bit A register, data from the absolute address specified by the -16-bit register HL, and stores the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{10000110}/\hex{86} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - Z = \faStar, N = 0, H = \faStar, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{ADD (HL)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: data}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{HL}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x86: - data = read(HL) - result, carry_per_bit = A + data - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 1 if carry_per_bit[3] else 0 - flags.C = 1 if carry_per_bit[7] else 0 -\end{verbatim} -\end{description} - -\subsection{ADD n: Add (immediate)} -\label{inst:ADD_n} - -Adds to the 8-bit A register, the immediate data \texttt{n}, and stores the -result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11000110}/\hex{C6} + \texttt{n} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - Z = \faStar, N = 0, H = \faStar, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{ADD n} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: n}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xC6: - n = read(PC++) - result, carry_per_bit = A + n - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 1 if carry_per_bit[3] else 0 - flags.C = 1 if carry_per_bit[7] else 0 -\end{verbatim} -\end{description} - -\subsection{ADC r: Add with carry (register)} -\label{inst:ADC_r} - -Adds to the 8-bit A register, the carry flag and the 8-bit register \texttt{r}, -and stores the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{10001xxx}/various - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - Z = \faStar, N = 0, H = \faStar, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{ADC r} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: ADC B -if opcode == 0x88: - result, carry_per_bit = A + flags.C + B - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 1 if carry_per_bit[3] else 0 - flags.C = 1 if carry_per_bit[7] else 0 -\end{verbatim} -\end{description} - -\subsection{ADC (HL): Add with carry (indirect HL)} -\label{inst:ADC_hl} - -Adds to the 8-bit A register, the carry flag and data from the absolute address -specified by the 16-bit register HL, and stores the result back into the A -register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{10001110}/\hex{8E} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - Z = \faStar, N = 0, H = \faStar, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{ADC (HL)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: data}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{HL}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x8E: - data = read(HL) - result, carry_per_bit = A + flags.C + data - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 1 if carry_per_bit[3] else 0 - flags.C = 1 if carry_per_bit[7] else 0 -\end{verbatim} -\end{description} - -\subsection{ADC n: Add with carry (immediate)} -\label{inst:ADC_n} - -Adds to the 8-bit A register, the carry flag and the immediate data \texttt{n}, -and stores the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11001110}/\hex{CE} + \texttt{n} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - Z = \faStar, N = 0, H = \faStar, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{ADC n} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: n}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xCE: - n = read(PC++) - result, carry_per_bit = A + flags.C + n - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 1 if carry_per_bit[3] else 0 - flags.C = 1 if carry_per_bit[7] else 0 -\end{verbatim} -\end{description} - -\subsection{SUB r: Subtract (register)} -\label{inst:SUB_r} - -Subtracts from the 8-bit A register, the 8-bit register \texttt{r}, and stores -the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{10010xxx}/various - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - Z = \faStar, N = 1, H = \faStar, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{SUB r} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: SUB B -if opcode == 0x90: - result, carry_per_bit = A - B - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 1 - flags.H = 1 if carry_per_bit[3] else 0 - flags.C = 1 if carry_per_bit[7] else 0 -\end{verbatim} -\end{description} - -\subsection{SUB (HL): Subtract (indirect HL)} -\label{inst:SUB_hl} - -Subtracts from the 8-bit A register, data from the absolute address specified -by the 16-bit register HL, and stores the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{10010110}/\hex{96} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - Z = \faStar, N = 1, H = \faStar, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{SUB (HL)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: data}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{HL}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x96: - data = read(HL) - result, carry_per_bit = A - data - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 1 - flags.H = 1 if carry_per_bit[3] else 0 - flags.C = 1 if carry_per_bit[7] else 0 -\end{verbatim} -\end{description} - -\subsection{SUB n: Subtract (immediate)} -\label{inst:SUB_n} - -Subtracts from the 8-bit A register, the immediate data \texttt{n}, and stores -the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11010110}/\hex{D6} + \texttt{n} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - Z = \faStar, N = 1, H = \faStar, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{SUB n} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: n}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xD6: - n = read(PC++) - result, carry_per_bit = A - n - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 1 - flags.H = 1 if carry_per_bit[3] else 0 - flags.C = 1 if carry_per_bit[7] else 0 -\end{verbatim} -\end{description} - -\subsection{SBC r: Subtract with carry (register)} -\label{inst:SBC_r} - -Subtracts from the 8-bit A register, the carry flag and the 8-bit register -\texttt{r}, and stores the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{10011xxx}/various - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - Z = \faStar, N = 1, H = \faStar, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{SBC r} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: SBC B -if opcode == 0x98: - result, carry_per_bit = A - flags.C - B - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 1 - flags.H = 1 if carry_per_bit[3] else 0 - flags.C = 1 if carry_per_bit[7] else 0 -\end{verbatim} -\end{description} - -\subsection{SBC (HL): Subtract with carry (indirect HL)} -\label{inst:SBC_hl} - -Subtracts from the 8-bit A register, the carry flag and data from the absolute -address specified by the 16-bit register HL, and stores the result back into -the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{10011110}/\hex{9E} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - Z = \faStar, N = 1, H = \faStar, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{SBC (HL)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: data}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{HL}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x9E: - data = read(HL) - result, carry_per_bit = A - flags.C - data - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 1 - flags.H = 1 if carry_per_bit[3] else 0 - flags.C = 1 if carry_per_bit[7] else 0 -\end{verbatim} -\end{description} - -\subsection{SBC n: Subtract with carry (immediate)} -\label{inst:SBC_n} - -Subtracts from the 8-bit A register, the carry flag and the immediate data -\texttt{n}, and stores the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11011110}/\hex{DE} + \texttt{n} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - Z = \faStar, N = 1, H = \faStar, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{SBC n} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: n}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xDE: - n = read(PC++) - result, carry_per_bit = A - flags.C - n - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 1 - flags.H = 1 if carry_per_bit[3] else 0 - flags.C = 1 if carry_per_bit[7] else 0 -\end{verbatim} -\end{description} - -\subsection{CP r: Compare (register)} -\label{inst:CP_r} - -Subtracts from the 8-bit A register, the 8-bit register \texttt{r}, and updates -flags based on the result. This instruction is basically identical to -\hyperref[inst:SUB_r]{SUB r}, but does not update the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{10111xxx}/various - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - Z = \faStar, N = 1, H = \faStar, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{CP r} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: CP B -if opcode == 0xB8: - result, carry_per_bit = A - B - flags.Z = 1 if result == 0 else 0 - flags.N = 1 - flags.H = 1 if carry_per_bit[3] else 0 - flags.C = 1 if carry_per_bit[7] else 0 -\end{verbatim} -\end{description} - -\subsection{CP (HL): Compare (indirect HL)} -\label{inst:CP_hl} - -Subtracts from the 8-bit A register, data from the absolute address specified -by the 16-bit register HL, and updates flags based on the result. This -instruction is basically identical to \hyperref[inst:SUB_hl]{SUB (HL)}, but -does not update the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{10111110}/\hex{BE} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - Z = \faStar, N = 1, H = \faStar, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{CP (HL)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: data}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{HL}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xBE: - data = read(HL) - result, carry_per_bit = A - data - flags.Z = 1 if result == 0 else 0 - flags.N = 1 - flags.H = 1 if carry_per_bit[3] else 0 - flags.C = 1 if carry_per_bit[7] else 0 -\end{verbatim} -\end{description} - -\subsection{CP n: Compare (immediate)} -\label{inst:CP_n} - -Subtracts from the 8-bit A register, the immediate data \texttt{n}, and updates -flags based on the result. This instruction is basically identical to -\hyperref[inst:SUB_n]{SUB n}, but does not update the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11111110}/\hex{FE} + \texttt{n} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - Z = \faStar, N = 1, H = \faStar, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{CP n} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: n}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xFE: - n = read(PC++) - result, carry_per_bit = A - n - flags.Z = 1 if result == 0 else 0 - flags.N = 1 - flags.H = 1 if carry_per_bit[3] else 0 - flags.C = 1 if carry_per_bit[7] else 0 -\end{verbatim} -\end{description} - -\subsection{INC r: Increment (register)} -\label{inst:INC_r} - -Increments data in the 8-bit register \texttt{r}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00xxx100}/various - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - Z = \faStar, N = 0, H = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{INC r} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: INC B -if opcode == 0x04: - result, carry_per_bit = B + 1 - B = result - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 1 if carry_per_bit[3] else 0 -\end{verbatim} -\end{description} - -\subsection{INC (HL): Increment (indirect HL)} -\label{inst:INC_hl} - -Increments data at the absolute address specified by the 16-bit register HL. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00110100}/\hex{34} - \item[Length] - 1 byte - \item[Duration] - 3 machine cycles - \item[Flags] - Z = \faStar, N = 0, H = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 24D{INC (HL)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: data} 8D{W: data}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{HL} 8D{HL}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x34: - data = read(HL) - result, carry_per_bit = data + 1 - write(HL, result) - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 1 if carry_per_bit[3] else 0 -\end{verbatim} -\end{description} - -\subsection{DEC r: Decrement (register)} -\label{inst:DEC_r} - -Decrements data in the 8-bit register \texttt{r}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00xxx101}/various - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - Z = \faStar, N = 1, H = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{DEC r} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: DEC B -if opcode == 0x05: - result, carry_per_bit = B - 1 - B = result - flags.Z = 1 if result == 0 else 0 - flags.N = 1 - flags.H = 1 if carry_per_bit[3] else 0 -\end{verbatim} -\end{description} - -\subsection{DEC (HL): Decrement (indirect HL)} -\label{inst:DEC_hl} - -Decrements data at the absolute address specified by the 16-bit register HL. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00110101}/\hex{35} - \item[Length] - 1 byte - \item[Duration] - 3 machine cycles - \item[Flags] - Z = \faStar, N = 1, H = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 24D{DEC (HL)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: data} 8D{W: data}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{HL} 8D{HL}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x35: - data = read(HL) - result, carry_per_bit = data - 1 - write(HL, result) - flags.Z = 1 if result == 0 else 0 - flags.N = 1 - flags.H = 1 if carry_per_bit[3] else 0 -\end{verbatim} -\end{description} - -\subsection{AND r: Bitwise AND (register)} -\label{inst:AND_r} - -Performs a bitwise AND operation between the 8-bit A register and the 8-bit register \texttt{r}, and stores the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{10100xxx}/various - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - Z = \faStar, N = 0, H = 1, C = 0 - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{AND r} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: AND B -if opcode == 0xA0: - result = A & B - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 1 - flags.C = 0 -\end{verbatim} -\end{description} - -\subsection{AND (HL): Bitwise AND (indirect HL)} -\label{inst:AND_hl} - -Performs a bitwise AND operation between the 8-bit A register and data from the absolute address specified by the 16-bit register HL, and stores the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{10100110}/\hex{A6} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - Z = \faStar, N = 0, H = 1, C = 0 - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{AND (HL)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: data}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{HL}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xA6: - data = read(HL) - result = A & data - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 1 - flags.C = 0 -\end{verbatim} -\end{description} - -\subsection{AND n: Bitwise AND (immediate)} -\label{inst:AND_n} - -Performs a bitwise AND operation between the 8-bit A register and immediate data \texttt{n}, and stores the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11100110}/\hex{E6} + \texttt{n} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - Z = \faStar, N = 0, H = 1, C = 0 - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{AND n} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: n}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xE6: - n = read(PC++) - result = A & n - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 1 - flags.C = 0 -\end{verbatim} -\end{description} - -\subsection{OR r: Bitwise OR (register)} -\label{inst:OR_r} - -Performs a bitwise OR operation between the 8-bit A register and the 8-bit register \texttt{r}, and stores the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{10110xxx}/various - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - Z = \faStar, N = 0, H = 0, C = 0 - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{OR r} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: OR B -if opcode == 0xB0: - result = A | B - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 0 - flags.C = 0 -\end{verbatim} -\end{description} - -\subsection{OR (HL): Bitwise OR (indirect HL)} -\label{inst:OR_hl} - -Performs a bitwise OR operation between the 8-bit A register and data from the absolute address specified by the 16-bit register HL, and stores the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{10110110}/\hex{B6} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - Z = \faStar, N = 0, H = 0, C = 0 - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{OR (HL)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: data}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{HL}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xB6: - data = read(HL) - result = A | data - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 0 - flags.C = 0 -\end{verbatim} -\end{description} - -\subsection{OR n: Bitwise OR (immediate)} -\label{inst:OR_n} - -Performs a bitwise OR operation between the 8-bit A register and immediate data \texttt{n}, and stores the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11110110}/\hex{F6} + \texttt{n} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - Z = \faStar, N = 0, H = 0, C = 0 - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{OR n} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: n}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xF6: - n = read(PC++) - result = A | n - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 0 - flags.C = 0 -\end{verbatim} -\end{description} - -\subsection{XOR r: Bitwise XOR (register)} -\label{inst:XOR_r} - -Performs a bitwise XOR operation between the 8-bit A register and the 8-bit register \texttt{r}, and stores the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{10101xxx}/various - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - Z = \faStar, N = 0, H = 0, C = 0 - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{XOR r} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -# example: XOR B -if opcode == 0xB8: - result = A ^ B - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 0 - flags.C = 0 -\end{verbatim} -\end{description} - -\subsection{XOR (HL): Bitwise XOR (indirect HL)} -\label{inst:XOR_hl} - -Performs a bitwise XOR operation between the 8-bit A register and data from the absolute address specified by the 16-bit register HL, and stores the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{10101110}/\hex{AE} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - Z = \faStar, N = 0, H = 0, C = 0 - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{XOR (HL)} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: data}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{HL}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xBE: - data = read(HL) - result = A ^ data - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 0 - flags.C = 0 -\end{verbatim} -\end{description} - -\subsection{XOR n: Bitwise XOR (immediate)} -\label{inst:XOR_n} - -Performs a bitwise XOR operation between the 8-bit A register and immediate data \texttt{n}, and stores the result back into the A register. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11101110}/\hex{EE} + \texttt{n} - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles - \item[Flags] - Z = \faStar, N = 0, H = 0, C = 0 - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{XOR n} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: n}; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1}; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xEE: - n = read(PC++) - result = A ^ n - A = result - flags.Z = 1 if result == 0 else 0 - flags.N = 0 - flags.H = 0 - flags.C = 0 -\end{verbatim} -\end{description} - -\subsection{CCF: Complement carry flag} -\label{inst:CCF} - -Flips the carry flag, and clears the N and H flags. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00111111}/\hex{3F} - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - N = 0, H = 0, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{CCF} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } -\item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x3F: - flags.N = 0 - flags.H = 0 - flags.C = ~flags.C -\end{verbatim} -\end{description} - -\subsection{SCF: Set carry flag} -\label{inst:SCF} - -Sets the carry flag, and clears the N and H flags. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00110111}/\hex{37} - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - N = 0, H = 0, C = 1 - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{SCF} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } -\item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x37: - flags.N = 0 - flags.H = 0 - flags.C = 1 -\end{verbatim} -\end{description} - -\subsection{DAA: Decimal adjust accumulator} -\label{inst:DAA} - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00100111}/\hex{27} - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - Z = \faStar, H = 0, C = \faStar - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{DAA} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } -\end{description} - -\subsection{CPL: Complement accumulator} -\label{inst:CPL} - -Flips all the bits in the 8-bit A register, and sets the N and H flags. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00101111}/\hex{2F} - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - N = 1, H = 1 - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{CPL} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } -\item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x2F: - A = ~A - flags.N = 1 - flags.H = 1 -\end{verbatim} -\end{description} - -\section{16-bit arithmetic instructions} - -\section{Rotate, shift, and bit operation instructions} - -\section{Control flow instructions} - -\subsection{JP nn: Jump} -\label{inst:JP} - -Unconditional jump to the absolute address specified by the 16-bit operand \texttt{nn}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode + data] - \bin{11000011}/\hex{C3} + LSB of \texttt{nn} + MSB of \texttt{nn} - \item[Length] - 3 bytes - \item[Duration] - 4 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4} 8D{M5/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 32D{JP nn} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: lsb(nn)} 8D{R: msb(nn)} 8U ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1} 8D{PC+2} 8U ; [opacity=0.4] 8D{nn} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xC3: - nn = unsigned_16(lsb=read(PC++), msb=read(PC++)) - PC = nn -\end{verbatim} -\end{description} - -\subsection{JP HL: Jump to HL} -\label{inst:JP_hl} - -Unconditional jump to the absolute address specified by the 16-bit register HL. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11101001}/\hex{E9} - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{JP HL} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{HL} X \\ - \end{tikztimingtable} - } - \item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xE9: - PC = HL -\end{verbatim} -\end{description} - -\begin{warning} - In some documentation this instruction is written as \texttt{JP [HL]}. This - is very misleading, since brackets are usually used to indicate a memory - read, and this instruction simply copies the value of HL to PC. -\end{warning} - -\subsection{JP cc, nn: Jump (conditional)} -\label{inst:JP_cc} - -Conditional jump to the absolute address specified by the 16-bit operand \texttt{nn}, depending on the condition \texttt{cc}. - -Note that the operand (absolute address) is read even when the condition is false! - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode + data] - \bin{110cc010}/various + LSB of \texttt{nn} + MSB of \texttt{nn} - \item[Length] - 3 bytes - \item[Duration] - 3 machine cycles (cc=false), or 4 machine cycles (cc=true) - \item[Flags] - - - \item[Timing (cc=false)] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 24D{JP cc, nn} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: lsb(nn)} 8D{R: msb(nn)} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1} 8D{PC+2} ; [opacity=0.4] 8D{PC+3} X \\ - \end{tikztimingtable} - } - \item[Timing (cc=true)] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4} 8D{M5/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 32D{JP cc, nn} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: lsb(nn)} 8D{R: msb(nn)} 8U ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1} 8D{PC+2} 8U ; [opacity=0.4] 8D{nn} X \\ - \end{tikztimingtable} - } -\item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode in [0xC2, 0xD2, 0xCA, 0xDA]: - nn = unsigned_16(lsb=read(PC++), msb=read(PC++)) - if F.check_condition(cc): - PC = nn -\end{verbatim} -\end{description} - -\subsection{JR e: Relative jump} -\label{inst:JR} - -Unconditional jump to the relative address specified by the signed 8-bit operand \texttt{e}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode + data] - \bin{00011000}/\hex{18} + offset \texttt{e} - \item[Length] - 2 bytes - \item[Duration] - 3 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 24D{JR e} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: e} 8U ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1} 8U ; [opacity=0.4] 8D{PC+2+e} X \\ - \end{tikztimingtable} - } -\item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x18: - e = signed_8(read(PC++)) - PC = PC + e -\end{verbatim} -\end{description} - -\subsection{JR cc, e: Relative jump (conditional)} -\label{inst:JR_cc} - -Conditional jump to the relative address specified by the signed 8-bit operand \texttt{e}, depending on the condition \texttt{cc}. - -Note that the operand (relative address offset) is read even when the condition is false! - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode + data] - \bin{001cc000}/various + offset \texttt{e} - \item[Length] - 2 bytes - \item[Duration] - 2 machine cycles (cc=false), or 3 machine cycles (cc=true) - \item[Flags] - - - \item[Timing (cc=false)] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{JR cc, e} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: e} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1}; [opacity=0.4] 8D{PC+2} X \\ - \end{tikztimingtable} - } - \item[Timing (cc=true)] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 24D{JR cc, e} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: e} 8U ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1} 8U ; [opacity=0.4] 8D{PC+2+e} X \\ - \end{tikztimingtable} - } -\item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode in [0x20, 0x30, 0x28, 0x38]: - e = signed_8(read(PC++)) - if F.check_condition(cc): - PC = PC + e -\end{verbatim} -\end{description} - -\subsection{CALL nn: Call function} -\label{inst:CALL} - -Unconditional function call to the absolute address specified by the 16-bit operand \texttt{nn}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode + data] - \bin{11001101}/\hex{CD} + LSB of \texttt{nn} + MSB of \texttt{nn} - \item[Length] - 3 bytes - \item[Duration] - 6 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4} 8D{M5} 8D{M6} 8D{M7/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 48D{CALL nn} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: lsb(nn)} 8D{R: msb(nn)} 8U 8D{W: msb(PC+3)} 8D{W: lsb(PC+3)} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1} 8D{PC+2} 8D{SP} 8D{SP-1} 8D{SP-2} ; [opacity=0.4] 8D{nn} X \\ - \end{tikztimingtable} - } -\item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xCD: - nn = unsigned_16(lsb=read(PC++), msb=read(PC++)) - SP-- - write(SP--, msb(PC)) - write(SP, lsb(PC)) - PC = nn -\end{verbatim} -\end{description} - -\subsection{CALL cc, nn: Call function (conditional)} -\label{inst:CALL_cc} - -Conditional function call to the absolute address specified by the 16-bit operand \texttt{nn}, depending on the condition \texttt{cc}. - -Note that the operand (absolute address) is read even when the condition is false! - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode + data] - \bin{110cc100}/various + LSB of \texttt{nn} + MSB of \texttt{nn} - \item[Length] - 3 bytes - \item[Duration] - 3 machine cycles (cc=false), or 6 machine cycles (cc=true) - \item[Flags] - - - \item[Timing (cc=false)] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 24D{CALL cc, nn} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: lsb(nn)} 8D{R: msb(nn)} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1} 8D{PC+2} ; [opacity=0.4] 8D{PC+3} X \\ - \end{tikztimingtable} - } - \item[Timing (cc=true)] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4} 8D{M5} 8D{M6} 8D{M7/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 48D{CALL cc, nn} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: lsb(nn)} 8D{R: msb(nn)} 8U 8D{W: msb(PC+3)} 8D{W: lsb(PC+3)} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{PC+1} 8D{PC+2} 8D{SP} 8D{SP-1} 8D{SP-2} ; [opacity=0.4] 8D{nn} X \\ - \end{tikztimingtable} - } -\item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode in [0xC4, 0xD4, 0xCC, 0xDC]: - nn = unsigned_16(lsb=read(PC++), msb=read(PC++)) - if F.check_condition(cc): - SP-- - write(SP--, msb(PC)) - write(SP, lsb(PC)) - PC = nn -\end{verbatim} -\end{description} - -\subsection{RET: Return from function} -\label{inst:RET} - -Unconditional return from a function. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11001001}/\hex{C9} - \item[Length] - 1 byte - \item[Duration] - 4 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4} 8D{M5/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 32D{RET} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: lsb(PC)} 8D{R: msb(PC)} 8U ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{SP} 8D{SP+1} 8U ; [opacity=0.4] 8D{new PC} X \\ - \end{tikztimingtable} - } -\item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xC9: - PC = unsigned_16(lsb=read(SP++), msb=read(SP++)) -\end{verbatim} -\end{description} - -\subsection{RET cc: Return from function (conditional)} -\label{inst:RET_cc} - -Conditional return from a function, depending on the condition \texttt{cc}. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{110cc000}/various - \item[Length] - 1 byte - \item[Duration] - 2 machine cycles (cc=false), or 5 machine cycles (cc=true) - \item[Flags] - - - \item[Timing (cc=false)] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 16D{RET cc} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8U ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8U ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } - \item[Timing (cc=true)] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4} 8D{M5} 8D{M6/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 40D{RET cc} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8U 8D{R: lsb(PC)} 8D{R: msb(PC)} 8U ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8U 8D{SP} 8D{SP+1} 8U ; [opacity=0.4] 8D{new PC} X \\ - \end{tikztimingtable} - } -\item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode in [0xC0, 0xD0, 0xC8, 0xD8]: - if F.check_condition(cc): - PC = unsigned_16(lsb=read(SP++), msb=read(SP++)) -\end{verbatim} -\end{description} - -\subsection{RETI: Return from interrupt handler} -\label{inst:RETI} - -Unconditional return from a function. Also enables interrupts by setting IME=1. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11011001}/\hex{D9} - \item[Length] - 1 byte - \item[Duration] - 4 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4} 8D{M5/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 32D{RETI} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8D{R: lsb(PC)} 8D{R: msb(PC)} 8U ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{SP} 8D{SP+1} 8U ; [opacity=0.4] 8D{new PC} X \\ - \end{tikztimingtable} - } -\item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xD9: - PC = unsigned_16(lsb=read(SP++), msb=read(SP++)) - IME = 1 -\end{verbatim} -\end{description} - -\subsection{RST n: Restart / Call function (implied)} -\label{inst:RST} - -Unconditional function call to the absolute fixed address defined by the opcode. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11xxx111}/various - \item[Length] - 1 byte - \item[Duration] - 4 machine cycles - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2} 8D{M3} 8D{M4} 8D{M5/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 32D{RST n} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} 8U 8D{W: msb(PC+1)} 8D{W: lsb(PC+1)} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} 8D{SP} 8D{SP-1} 8D{SP-2} ; [opacity=0.4] 8D{new PC} X \\ - \end{tikztimingtable} - } -\item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode in [0xC7, 0xD7, 0xE7, 0xF7, 0xCF, 0xDF, 0xEF, 0xFF]: - n = rst_address(opcode) - SP-- - write(SP--, msb(PC)) - write(SP, lsb(PC)) - PC = unsigned_16(lsb=n, msb=0x00) -\end{verbatim} -\end{description} - -\section{Miscellaneous instructions} - -\subsection{HALT: Halt system clock} -\label{inst:HALT} - -\subsection{STOP: Stop system and main clocks} -\label{inst:STOP} - -\subsection{DI: Disable interrupts} -\label{inst:DI} - -Disables interrupt handling by setting IME=0 and cancelling any scheduled -effects of the EI instruction if any. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11110011}/\hex{F3} - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{DI} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } -\item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xF3: - IME = 0 -\end{verbatim} -\end{description} - -\subsection{EI: Enable interrupts} -\label{inst:EI} - -Schedules interrupt handling to be enabled after the next machine cycle. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{11111011}/\hex{FB} - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle (+ 1 machine cycle for the effect) - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{EI} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } -\item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0xFB: - IME_scheduled = true -\end{verbatim} -\end{description} - -\subsection{NOP: No operation} -\label{inst:NOP} - -No operation. This instruction doesn't do anything, but can be used to add a -delay of one machine cycle and increment PC by one. - -\begin{description}[leftmargin=9em, style=nextline] - \item[Opcode] - \bin{00000000}/\hex{00} - \item[Length] - 1 byte - \item[Duration] - 1 machine cycle - \item[Flags] - - - \item[Timing] \parbox{\linewidth}{ - \begin{tikztimingtable}[timing/wscale=0.8] - M-cycle & X 8D{M1} 8D{M2/M1} X \\ - Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{NOP} ; [opacity=0.4] X \\ - Mem R/W & X 8D{R: opcode} ; [opacity=0.4] 8D{R: next op} X \\ - Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\ - \end{tikztimingtable} - } -\item[Pseudocode] \begin{verbatim} -opcode = read(PC++) -if opcode == 0x00: - // nothing -\end{verbatim} -\end{description} - -\end{document} diff --git a/chapter/cpu/instruction-set.typ b/chapter/cpu/instruction-set.typ new file mode 100644 index 0000000..b31fee7 --- /dev/null +++ b/chapter/cpu/instruction-set.typ @@ -0,0 +1,1854 @@ +#import "../../common.typ": * +#import "../../timing.typ" + +#let ops = toml("../../opcodes.toml") + +#let instruction-block(body, ..grid-args) = block(breakable: false)[ + #body + #grid( + columns: (6em, auto), + gutter: 8pt, + ..grid-args + ) +] + +#let instruction-timing(mnemonic: str, duration: int, mem_rw: array, mem_addr: array, next_addr: content) = timing.diagram(w_scale: 0.75, ..{ + import timing: * + ( + (label: "M-cycle", wave: ( + x(1), + ..range(1 + duration).map((cycle) => { + let m_cycle = cycle + 1 + if m_cycle == duration + 1 { + d(8, "M" + str(m_cycle) + "/M1") + } else { + d(8, "M" + str(m_cycle)) + } + }), + x(1), + )), + (label: "Instruction", wave: ( + d(9, [Previous], opacity: 40%), + d(duration * 8, mnemonic), + x(1, opacity: 40%), + )), + (label: "Mem R/W", wave: ( + x(1), + timing.d(8, [R: opcode]), + ..mem_rw.map((label) => if label == "U" { timing.u(8) } else { timing.d(8, label) }), + timing.d(8, [R: next op], opacity: 40%), + x(1, opacity: 40%), + )), + (label: "Mem addr", wave: ( + x(1), + timing.d(8, [PC#sub[0]]), + ..mem_addr.map((label) => if label == "U" { timing.u(8) } else { timing.d(8, label) }), + timing.d(8, next_addr, opacity: 40%), + x(1, opacity: 40%), + )), + ) +}) + +#let instruction = (body, mnemonic: str, length: int, duration: int, opcode: content, flags: [-], mem_rw: array, mem_addr: array, next_addr: [], pseudocode: content) => instruction-block( + body, + [*Opcode*], opcode, + [*Length*], if length > 1 { str(length) + " bytes" } else { "1 byte" }, + [*Duration*], if duration > 1 { str(duration) + " machine cycles" } else { "1 machine cycle" }, + [*Flags*], flags, + [*Timing*], instruction-timing(mnemonic: mnemonic, duration: duration, mem_rw: mem_rw, mem_addr: mem_addr, next_addr: if next_addr == [] { [PC#sub[0]+#length] } else { next_addr }), + [*Pseudocode*], pseudocode, +) + +#let flag-update = awesome[\u{f005}] + +== Sharp SM83 instruction set + +=== Overview + +==== CB opcode prefix +==== Undefined opcodes + +=== 8-bit load instructions + +#instruction( + [ + ==== LD r, r': Load register (register) + + Load to the 8-bit register `r`, data from the 8-bit register `r'`. + ], + mnemonic: "LD r, r'", + length: 1, + duration: 1, + opcode: [#bin("01xxxyyy")/various], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +opcode = read(PC++) +# example: LD B, C +if opcode == 0x41: + B = C + ``` +) + +#instruction( + [ + ==== LD r, n: Load register (immediate) + + Load to the 8-bit register `r`, the immediate data `n`. + ], + mnemonic: "LD r, n", + length: 2, + duration: 2, + opcode: [#bin("00xxx110")/various + `n`], + mem_rw: ([R: `n`],), + mem_addr: ([PC#sub[0]+1],), + pseudocode: ``` +opcode = read(PC++) +# example: LD B, n +if opcode == 0x06: + B = read(PC++) + ``` +) + +#instruction( + [ + ==== LD r, (HL): Load register (indirect HL) + + Load to the 8-bit register `r`, data from the absolute address specified by the 16-bit register HL. + ], + mnemonic: "LD r, (HL)", + length: 1, + duration: 2, + opcode: [#bin("01xxx110")/various], + mem_rw: ([R: data],), + mem_addr: ([HL],), + pseudocode: ``` +opcode = read(PC++) +# example: LD B, (HL) +if opcode == 0x46: + B = read(HL) + ``` +) + +#instruction( + [ + ==== LD (HL), r: Load from register (indirect HL) + + Load to the absolute address specified by the 16-bit register HL, data from the 8-bit register `r`. + ], + mnemonic: "LD (HL), r", + length: 1, + duration: 2, + opcode: [#bin("01110xxx")/various], + mem_rw: ([W: data],), + mem_addr: ([HL],), + pseudocode: ``` +opcode = read(PC++) +# example: LD (HL), B +if opcode == 0x70: + write(HL, B) + ``` +) + +#instruction( + [ + ==== LD (HL), n: Load from immediate data (indirect HL) + + Load to the absolute address specified by the 16-bit register HL, the immediate data `n`. + ], + mnemonic: "LD (HL), n", + length: 2, + duration: 3, + opcode: [#bin("00110110")/#hex("36") + `n`], + mem_rw: ([R: `n`], [W: `n`],), + mem_addr: ([PC#sub[0]+1], [HL],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x36: + n = read(PC++) + write(HL, n) + ``` +) + +#instruction( + [ + ==== LD A, (BC): Load accumulator (indirect BC) + + Load to the 8-bit A register, data from the absolute address specified by the 16-bit register BC. + ], + mnemonic: "LD A, (BC)", + length: 1, + duration: 2, + opcode: [#bin("00001010")/#hex("0A")], + mem_rw: ([R: data],), + mem_addr: ([BC],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x0A: + A = read(BC) + ``` +) + +#instruction( + [ + ==== LD A, (DE): Load accumulator (indirect DE) + + Load to the 8-bit A register, data from the absolute address specified by the 16-bit register DE. + ], + mnemonic: "LD A, (DE)", + length: 1, + duration: 2, + opcode: [#bin("00011010")/#hex("1A")], + mem_rw: ([R: data],), + mem_addr: ([DE],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x1A: + A = read(DE) + ``` +) + +#instruction( + [ + ==== LD (BC), A: Load from accumulator (indirect BC) + + Load to the absolute address specified by the 16-bit register BC, data from the 8-bit A register. + ], + mnemonic: "LD (BC), A", + length: 1, + duration: 2, + opcode: [#bin("00000010")/#hex("02")], + mem_rw: ([W: data],), + mem_addr: ([BC],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x02: + write(BC, A) + ``` +) + +#instruction( + [ + ==== LD (DE), A: Load from accumulator (indirect DE) + + Load to the absolute address specified by the 16-bit register DE, data from the 8-bit A register. + ], + mnemonic: "LD (DE), A", + length: 1, + duration: 2, + opcode: [#bin("00010010")/#hex("12")], + mem_rw: ([W: data],), + mem_addr: ([DE],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x12: + write(DE, A) + ``` +) + +#instruction( + [ + ==== LD A, (nn): Load accumulator (direct) + + Load to the 8-bit A register, data from the absolute address specified by the 16-bit operand `nn`. + ], + mnemonic: "LD A, (nn)", + length: 3, + duration: 4, + opcode: [#bin("11111010")/#hex("FA") + LSB of `n` + MSB of `n`], + mem_rw: ([R: lsb(`nn`)], [R: msb(`nn`)], [R: data],), + mem_addr: ([PC#sub[0]+1], [PC#sub[0]+2], [`nn`]), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xFA: + nn = unsigned_16(lsb=read(PC++), msb=read(PC++)) + A = read(nn) + ``` +) + +#instruction( + [ + ==== LD (nn), A: Load from accumulator (direct) + + Load to the absolute address specified by the 16-bit operand `nn`, data from the 8-bit A register. + ], + mnemonic: "LD (nn), A", + length: 3, + duration: 4, + opcode: [#bin("11101010")/#hex("EA") + LSB of `n` + MSB of `n`], + mem_rw: ([R: lsb(`nn`)], [R: msb(`nn`)], [W: data],), + mem_addr: ([PC#sub[0]+1], [PC#sub[0]+2], [`nn`]), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xEA: + nn = unsigned_16(lsb=read(PC++), msb=read(PC++)) + write(nn, A) + ``` +) + +#instruction( + [ + ==== LDH A, (C): Load accumulator (indirect #hex("FF00")+C) + + Load to the 8-bit A register, data from the address specified by the 8-bit C register. The full 16-bit absolute address is obtained by setting the most significant byte to #hex("FF") and the least significant byte to the value of C, so the possible range is #hex-range("FF00", "FFFF"). + ], + mnemonic: "LDH A, (C)", + length: 1, + duration: 2, + opcode: [#bin("11110010")/#hex("F2")], + mem_rw: ([R: A],), + mem_addr: ([#hex("FF00")+C],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xE2: + A = read(unsigned_16(lsb=C, msb=0xFF)) + ``` +) + +#instruction( + [ + ==== LDH (C), A: Load from accumulator (indirect #hex("FF00")+C) + + Load to the address specified by the 8-bit C register, data from the 8-bit A register. The full 16-bit absolute address is obtained by setting the most significant byte to #hex("FF") and the least significant byte to the value of C, so the possible range is #hex-range("FF00", "FFFF"). + ], + mnemonic: "LDH (C), A", + length: 1, + duration: 2, + opcode: [#bin("11100010")/#hex("E2")], + mem_rw: ([W: A],), + mem_addr: ([#hex("FF00")+C],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xE2: + write(unsigned_16(lsb=C, msb=0xFF), A) + ``` +) + +#instruction( + [ + ==== LDH A, (n): Load accumulator (direct #hex("FF00")+n) + + Load to the 8-bit A register, data from the address specified by the 8-bit immediate data `n`. The full 16-bit absolute address is obtained by setting the most significant byte to #hex("FF") and the least significant byte to the value of `n`, so the possible range is #hex-range("FF00", "FFFF"). + ], + mnemonic: "LDH A, (n)", + length: 2, + duration: 3, + opcode: [#bin("11110000")/#hex("F0")], + mem_rw: ([R: `n`], [R: A],), + mem_addr: ([PC#sub[0]+1], [#hex("FF00")+`n`],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xF0: + n = read(PC++) + A = read(unsigned_16(lsb=n, msb=0xFF)) + ``` +) + +#instruction( + [ + ==== LDH (n), A: Load from accumulator (direct #hex("FF00")+n) + + Load to the address specified by the 8-bit immediate data `n`, data from the 8-bit A register. The full 16-bit absolute address is obtained by setting the most significant byte to #hex("FF") and the least significant byte to the value of `n`, so the possible range is #hex-range("FF00", "FFFF"). + ], + mnemonic: "LDH (n), A", + length: 2, + duration: 3, + opcode: [#bin("11100000")/#hex("E0")], + mem_rw: ([R: `n`], [W: A],), + mem_addr: ([PC#sub[0]+1], [#hex("FF00")+C],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xE0: + n = read(PC++) + write(unsigned_16(lsb=n, msb=0xFF), A) + ``` +) + +#instruction( + [ + ==== LD A, (HL-): Load accumulator (indirect HL, decrement) + + Load to the 8-bit A register, data from the absolute address specified by the 16-bit register HL. The value of HL is decremented after the memory read. + ], + mnemonic: "LD A, (HL-)", + length: 1, + duration: 2, + opcode: [#bin("00111010")/#hex("3A")], + mem_rw: ([R: A],), + mem_addr: ([HL],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x3A: + A = read(HL--) + ``` +) + +#instruction( + [ + ==== LD (HL-), A: Load from accumulator (indirect HL, decrement) + + Load to the absolute address specified by the 16-bit register HL, data from the 8-bit A register. The value of HL is decremented after the memory write. + ], + mnemonic: "LD (HL-), A", + length: 1, + duration: 2, + opcode: [#bin("00110010")/#hex("32")], + mem_rw: ([W: A],), + mem_addr: ([HL],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x32: + write(HL--, A) + ``` +) + +#instruction( + [ + ==== LD A, (HL+): Load accumulator (indirect HL, increment) + + Load to the 8-bit A register, data from the absolute address specified by the 16-bit register HL. The value of HL is incremented after the memory read. + ], + mnemonic: "LD A, (HL+)", + length: 1, + duration: 2, + opcode: [#bin("00101010")/#hex("2A")], + mem_rw: ([R: A],), + mem_addr: ([HL],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x2A: + A = read(HL++) + ``` +) + +#instruction( + [ + ==== LD (HL+), A: Load from accumulator (indirect HL, increment) + + Load to the absolute address specified by the 16-bit register HL, data from the 8-bit A register. The value of HL is decremented after the memory write. + ], + mnemonic: "LD (HL+), A", + length: 1, + duration: 2, + opcode: [#bin("00100010")/#hex("22")], + mem_rw: ([W: A],), + mem_addr: ([HL],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x32: + write(HL++, A) + ``` +) + +=== 16-bit load instructions + +#instruction( + [ + ==== LD rr, nn: Load 16-bit register / register pair + + Load to the 16-bit register `rr`, the immediate 16-bit data `nn`. + ], + mnemonic: "LD rr, nn", + length: 3, + duration: 3, + opcode: [#bin("00xx0001")/various + LSB of `nn` + MSB of `nn`], + mem_rw: ([R: lsb(`nn`)], [R: msb(`nn`)],), + mem_addr: ([PC#sub[0]+1], [PC#sub[0]+2],), + pseudocode: ``` +opcode = read(PC++) +# example: LD BC, nn +if opcode == 0x01: + nn = unsigned_16(lsb=read(PC++), msb=read(PC++)) + BC = nn + ``` +) + +#instruction( + [ + ==== LD (nn), SP: Load from stack pointer (direct) + + Load to the absolute address specified by the 16-bit operand `nn`, data from the 16-bit SP register. + ], + mnemonic: "LD (nn), SP", + length: 3, + duration: 5, + opcode: [#bin("00001000")/#hex("08") + LSB of `nn` + MSB of `nn`], + mem_rw: ([R: lsb(`nn`)], [R: msb(`nn`)], [W: lsb(SP)], [W: msb(SP)],), + mem_addr: ([PC#sub[0]+1], [PC#sub[0]+2], [`nn`], [`nn`+1]), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x08: + nn = unsigned_16(lsb=read(PC++), msb=read(PC++)) + write(nn, lsb(SP)) + write(nn+1, msb(SP)) + ``` +) + +#instruction( + [ + ==== LD SP, HL: Load stack pointer from HL + + Load to the 16-bit SP register, data from the 16-bit HL register. + ], + mnemonic: "LD SP, HL", + length: 1, + duration: 2, + opcode: [#bin("11111001")/#hex("F9")], + mem_rw: ("U",), + mem_addr: ("U",), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xF9: + SP = HL + ``` +) + +#instruction( + [ + ==== PUSH rr: Push to stack + + Push to the stack memory, data from the 16-bit register `rr`. + ], + mnemonic: "PUSH rr", + length: 1, + duration: 4, + opcode: [#bin("11xx0101")/various], + mem_rw: ("U", [W: msb(`rr`)], [W: lsb(`rr`)],), + mem_addr: ([SP#sub[0]], [SP#sub[0]-1], [SP#sub[0]-2],), + pseudocode: ``` +opcode = read(PC++) +# example: PUSH BC +if opcode == 0xC5: + SP-- + write(SP--, msb(BC)) + write(SP, lsb(BC)) + ``` +) + +#instruction( + [ + ==== POP rr: Pop from stack + + Pops to the 16-bit register `rr`, data from the stack memory. + + This instruction does not do calculations that affect flags, but POP AF completely replaces the F register value, so all flags are changed based on the 8-bit data that is read from memory. + ], + mnemonic: "POP rr", + length: 1, + duration: 3, + flags: [See the instruction description], + opcode: [#bin("11xx0001")/various], + mem_rw: ([R: lsb(`rr`)], [R: msb(`rr`)],), + mem_addr: ([SP#sub[0]], [SP#sub[0]-1], [SP#sub[0]-2],), + pseudocode: ``` +opcode = read(PC++) +# example: POP BC +if opcode == 0xC1: + BC = unsigned_16(lsb=read(SP++), msb=read(SP++)) + ``` +) + +==== LD HL,SP+e + +TODO + +=== 8-bit arithmetic and logical instructions + +#instruction( + [ + ==== ADD r: Add (register) + + Adds to the 8-bit A register, the 8-bit register `r`, and stores the result back into the A register. + ], + mnemonic: "ADD r", + length: 1, + duration: 1, + flags: [Z = #flag-update, N = 0, H = #flag-update, C = #flag-update], + opcode: [#bin("10000xxx")/various], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +opcode = read(PC++) +# example: ADD B +if opcode == 0x80: + result, carry_per_bit = A + B + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 1 if carry_per_bit[3] else 0 + flags.C = 1 if carry_per_bit[7] else 0 + ``` +) + +#instruction( + [ + ==== ADD (HL): Add (indirect HL) + + Adds to the 8-bit A register, data from the absolute address specified by the 16-bit register HL, and stores the result back into the A register. + ], + mnemonic: "ADD (HL)", + length: 1, + duration: 2, + flags: [Z = #flag-update, N = 0, H = #flag-update, C = #flag-update], + opcode: [#bin("10000110")/#hex("86")], + mem_rw: ([R: data],), + mem_addr: ([HL],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x86: + data = read(HL) + result, carry_per_bit = A + data + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 1 if carry_per_bit[3] else 0 + flags.C = 1 if carry_per_bit[7] else 0 + ``` +) + +#instruction( + [ + ==== ADD n: Add (immediate) + + Adds to the 8-bit A register, the immediate data `n`, and stores the result back into the A register. + ], + mnemonic: "ADD n", + length: 1, + duration: 2, + flags: [Z = #flag-update, N = 0, H = #flag-update, C = #flag-update], + opcode: [#bin("11000110")/#hex("C6")], + mem_rw: ([R: `n`],), + mem_addr: ([PC#sub[0]+1],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xC6: + n = read(PC++) + result, carry_per_bit = A + n + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 1 if carry_per_bit[3] else 0 + flags.C = 1 if carry_per_bit[7] else 0 + ``` +) + +#instruction( + [ + ==== ADC r: Add with carry (register) + + Adds to the 8-bit A register, the carry flag and the 8-bit register `r`, and stores the result back into the A register. + + ], + mnemonic: "ADC r", + length: 1, + duration: 1, + flags: [Z = #flag-update, N = 0, H = #flag-update, C = #flag-update], + opcode: [#bin("10001xxx")/various], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +opcode = read(PC++) +# example: ADC B +if opcode == 0x88: + result, carry_per_bit = A + flags.C + B + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 1 if carry_per_bit[3] else 0 + flags.C = 1 if carry_per_bit[7] else 0 + ``` +) + +#instruction( + [ + ==== ADC (HL): Add with carry (indirect HL) + + Adds to the 8-bit A register, the carry flag and data from the absolute address specified by the 16-bit register HL, and stores the result back into the A register. + ], + mnemonic: "ADC (HL)", + length: 1, + duration: 2, + flags: [Z = #flag-update, N = 0, H = #flag-update, C = #flag-update], + opcode: [#bin("10001110")/#hex("8E")], + mem_rw: ([R: data],), + mem_addr: ([HL],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x8E: + data = read(HL) + result, carry_per_bit = A + flags.C + data + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 1 if carry_per_bit[3] else 0 + flags.C = 1 if carry_per_bit[7] else 0 + ``` +) + +#instruction( + [ + ==== ADC n: Add with carry (immediate) + + Adds to the 8-bit A register, the carry flag and the immediate data `n`, and stores the result back into the A register. + ], + mnemonic: "ADC n", + length: 1, + duration: 2, + flags: [Z = #flag-update, N = 0, H = #flag-update, C = #flag-update], + opcode: [#bin("11001110")/#hex("CE") + `n`], + mem_rw: ([R: `n`],), + mem_addr: ([PC#sub[0]+1],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xCE: + n = read(PC++) + result, carry_per_bit = A + flags.C + n + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 1 if carry_per_bit[3] else 0 + flags.C = 1 if carry_per_bit[7] else 0 + ``` +) + +#instruction( + [ + ==== SUB r: Subtract (register) + + Subtracts from the 8-bit A register, the 8-bit register `r`, and stores the result back into the A register. + ], + mnemonic: "SUB r", + length: 1, + duration: 1, + flags: [Z = #flag-update, N = 1, H = #flag-update, C = #flag-update], + opcode: [#bin("10010xxx")/various], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +opcode = read(PC++) +# example: SUB B +if opcode == 0x90: + result, carry_per_bit = A - B + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 1 + flags.H = 1 if carry_per_bit[3] else 0 + flags.C = 1 if carry_per_bit[7] else 0 + ``` +) + +#instruction( + [ + ==== SUB (HL): Subtract (indirect HL) + + Subtracts from the 8-bit A register, data from the absolute address specified by the 16-bit register HL, and stores the result back into the A register. + ], + mnemonic: "SUB (HL)", + length: 1, + duration: 2, + flags: [Z = #flag-update, N = 1, H = #flag-update, C = #flag-update], + opcode: [#bin("10010110")/#hex("96")], + mem_rw: ([R: data],), + mem_addr: ([HL],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x96: + data = read(HL) + result, carry_per_bit = A - data + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 1 + flags.H = 1 if carry_per_bit[3] else 0 + flags.C = 1 if carry_per_bit[7] else 0 + ``` +) + +#instruction( + [ + ==== SUB n: Subtract (immediate) + + Subtracts from the 8-bit A register, the immediate data `n`, and stores the result back into the A register. + ], + mnemonic: "SUB n", + length: 1, + duration: 2, + flags: [Z = #flag-update, N = 1, H = #flag-update, C = #flag-update], + opcode: [#bin("11010110")/#hex("D6") + `n`], + mem_rw: ([R: `n`],), + mem_addr: ([PC#sub[0]+1],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xD6: + n = read(PC++) + result, carry_per_bit = A - n + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 1 + flags.H = 1 if carry_per_bit[3] else 0 + flags.C = 1 if carry_per_bit[7] else 0 + ``` +) + +#instruction( + [ + ==== SBC r: Subtract with carry (register) + + Subtracts from the 8-bit A register, the carry flag and the 8-bit register `r`, and stores the result back into the A register. + ], + mnemonic: "SBC r", + length: 1, + duration: 1, + flags: [Z = #flag-update, N = 1, H = #flag-update, C = #flag-update], + opcode: [#bin("10011xxx")/various], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +opcode = read(PC++) +# example: SBC B +if opcode == 0x98: + result, carry_per_bit = A - flags.C - B + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 1 + flags.H = 1 if carry_per_bit[3] else 0 + flags.C = 1 if carry_per_bit[7] else 0 + ``` +) + +#instruction( + [ + ==== SBC (HL): Subtract with carry (indirect HL) + + Subtracts from the 8-bit A register, the carry flag and data from the absolute address specified by the 16-bit register HL, and stores the result back into the A register. + ], + mnemonic: "SBC (HL)", + length: 1, + duration: 2, + flags: [Z = #flag-update, N = 1, H = #flag-update, C = #flag-update], + opcode: [#bin("10011110")/#hex("9E")], + mem_rw: ([R: data],), + mem_addr: ([HL],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x9E: + data = read(HL) + result, carry_per_bit = A - flags.C - data + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 1 + flags.H = 1 if carry_per_bit[3] else 0 + flags.C = 1 if carry_per_bit[7] else 0 + ``` +) + +#instruction( + [ + ==== SBC n: Subtract with carry (immediate) + + Subtracts from the 8-bit A register, the carry flag and the immediate data `n`, and stores the result back into the A register. + ], + mnemonic: "SBC n", + length: 1, + duration: 2, + flags: [Z = #flag-update, N = 1, H = #flag-update, C = #flag-update], + opcode: [#bin("11011110")/#hex("DE") + `n`], + mem_rw: ([R: `n`],), + mem_addr: ([HL],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xDE: + n = read(PC++) + result, carry_per_bit = A - flags.C - n + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 1 + flags.H = 1 if carry_per_bit[3] else 0 + flags.C = 1 if carry_per_bit[7] else 0 + ``` +) + +#instruction( + [ + ==== CP r: Compare (register) + + Subtracts from the 8-bit A register, the 8-bit register `r`, and updates flags based on the result. This instruction is basically identical to #link()[SUB r], but does not update the A register. + ], + mnemonic: "CP r", + length: 1, + duration: 1, + flags: [Z = #flag-update, N = 1, H = #flag-update, C = #flag-update], + opcode: [#bin("10111xxx")/various], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +opcode = read(PC++) +# example: CP B +if opcode == 0xB8: + result, carry_per_bit = A - B + flags.Z = 1 if result == 0 else 0 + flags.N = 1 + flags.H = 1 if carry_per_bit[3] else 0 + flags.C = 1 if carry_per_bit[7] else 0 + ``` +) + +#instruction( + [ + ==== CP (HL): Compare (indirect HL) + + Subtracts from the 8-bit A register, data from the absolute address specified by the 16-bit register HL, and updates flags based on the result. This instruction is basically identical to #link()[SUB (HL)], but does not update the A register. + ], + mnemonic: "CP (HL)", + length: 1, + duration: 2, + flags: [Z = #flag-update, N = 1, H = #flag-update, C = #flag-update], + opcode: [#bin("10011110")/#hex("9E")], + mem_rw: ([R: data],), + mem_addr: ([HL],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xBE: + data = read(HL) + result, carry_per_bit = A - data + flags.Z = 1 if result == 0 else 0 + flags.N = 1 + flags.H = 1 if carry_per_bit[3] else 0 + flags.C = 1 if carry_per_bit[7] else 0 + ``` +) + +#instruction( + [ + ==== CP n: Compare (immediate) + + Subtracts from the 8-bit A register, the immediate data `n`, and updates flags based on the result. This instruction is basically identical to #link()[SUB n], but does not update the A register. + ], + mnemonic: "CP n", + length: 1, + duration: 2, + flags: [Z = #flag-update, N = 1, H = #flag-update, C = #flag-update], + opcode: [#bin("11111110")/#hex("FE") + `n`], + mem_rw: ([R: `n`],), + mem_addr: ([PC#sub[0]+1],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xFE: + n = read(PC++) + result, carry_per_bit = A - n + flags.Z = 1 if result == 0 else 0 + flags.N = 1 + flags.H = 1 if carry_per_bit[3] else 0 + flags.C = 1 if carry_per_bit[7] else 0 + ``` +) + +#instruction( + [ + ==== INC r: Increment (register) + + Increments data in the 8-bit register `r`. + ], + mnemonic: "INC r", + length: 1, + duration: 1, + flags: [Z = #flag-update, N = 0, H = #flag-update], + opcode: [#bin("00xxx100")/various], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +opcode = read(PC++) +# example: INC B +if opcode == 0x04: + result, carry_per_bit = B + 1 + B = result + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 1 if carry_per_bit[3] else 0 + ``` +) + +#instruction( + [ + ==== INC (HL): Increment (indirect HL) + + Increments data at the absolute address specified by the 16-bit register HL. + ], + mnemonic: "INC (HL)", + length: 1, + duration: 3, + flags: [Z = #flag-update, N = 0, H = #flag-update], + opcode: [#bin("00110100")/#hex("34")], + mem_rw: ([R: data], [W: data],), + mem_addr: ([HL], [HL],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x34: + data = read(HL) + result, carry_per_bit = data + 1 + write(HL, result) + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 1 if carry_per_bit[3] else 0 + ``` +) + +#instruction( + [ + ==== DEC r: Decrement (register) + + Increments data in the 8-bit register `r`. + ], + mnemonic: "DEC r", + length: 1, + duration: 1, + flags: [Z = #flag-update, N = 1, H = #flag-update], + opcode: [#bin("00xxx101")/various], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +opcode = read(PC++) +# example: DEC B +if opcode == 0x05: + result, carry_per_bit = B - 1 + B = result + flags.Z = 1 if result == 0 else 0 + flags.N = 1 + flags.H = 1 if carry_per_bit[3] else 0 + ``` +) + +#instruction( + [ + ==== DEC (HL): Decrement (indirect HL) + + Decrements data at the absolute address specified by the 16-bit register HL. + ], + mnemonic: "DEC (HL)", + length: 1, + duration: 3, + flags: [Z = #flag-update, N = 1, H = #flag-update], + opcode: [#bin("00110101")/#hex("35")], + mem_rw: ([R: data], [W: data],), + mem_addr: ([HL], [HL],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x35: + data = read(HL) + result, carry_per_bit = data - 1 + write(HL, result) + flags.Z = 1 if result == 0 else 0 + flags.N = 1 + flags.H = 1 if carry_per_bit[3] else 0 + ``` +) + +#instruction( + [ + ==== AND r: Bitwise AND (register) + + Performs a bitwise AND operation between the 8-bit A register and the 8-bit register `r`, and stores the result back into the A register. + ], + mnemonic: "AND r", + length: 1, + duration: 1, + flags: [Z = #flag-update, N = 0, H = 1, C = 0], + opcode: [#bin("10100xxx")/various], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +opcode = read(PC++) +# example: AND B +if opcode == 0xA0: + result = A & B + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 1 + flags.C = 0 + ``` +) + +#instruction( + [ + ==== AND (HL): Bitwise AND (indirect HL) + + Performs a bitwise AND operation between the 8-bit A register and data from the absolute address specified by the 16-bit register HL, and stores the result back into the A register. + ], + mnemonic: "AND (HL)", + length: 1, + duration: 2, + flags: [Z = #flag-update, N = 0, H = 1, C = 0], + opcode: [#bin("10100110")/#hex("A6")], + mem_rw: ([R: data],), + mem_addr: ([HL],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xA6: + data = read(HL) + result = A & data + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 1 + flags.C = 0 + ``` +) + +#instruction( + [ + ==== AND n: Bitwise AND (immediate) + + Performs a bitwise AND operation between the 8-bit A register and immediate data `n`, and stores the result back into the A register. + ], + mnemonic: "AND n", + length: 1, + duration: 2, + flags: [Z = #flag-update, N = 0, H = 1, C = 0], + opcode: [#bin("11100110")/#hex("E6") + `n`], + mem_rw: ([R: `n`],), + mem_addr: ([PC#sub[0]+1],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xE6: + n = read(PC++) + result = A & n + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 1 + flags.C = 0 + ``` +) + +#instruction( + [ + ==== OR r: Bitwise OR (register) + + Performs a bitwise OR operation between the 8-bit A register and the 8-bit register `r`, and stores the result back into the A register. + ], + mnemonic: "OR r", + length: 1, + duration: 1, + flags: [Z = #flag-update, N = 0, H = 0, C = 0], + opcode: [#bin("10110xxx")/various], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +opcode = read(PC++) +# example: OR B +if opcode == 0xB0: + result = A | B + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 0 + flags.C = 0 + ``` +) + +#instruction( + [ + ==== OR (HL): Bitwise OR (indirect HL) + + Performs a bitwise OR operation between the 8-bit A register and data from the absolute address specified by the 16-bit register HL, and stores the result back into the A register. + ], + mnemonic: "OR (HL)", + length: 1, + duration: 2, + flags: [Z = #flag-update, N = 0, H = 0, C = 0], + opcode: [#bin("10110110")/#hex("B6")], + mem_rw: ([R: data],), + mem_addr: ([HL],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xB6: + data = read(HL) + result = A | data + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 0 + flags.C = 0 + ``` +) + +#instruction( + [ + ==== OR n: Bitwise OR (immediate) + + Performs a bitwise OR operation between the 8-bit A register and immediate data `n`, and stores the result back into the A register. + ], + mnemonic: "OR n", + length: 1, + duration: 2, + flags: [Z = #flag-update, N = 0, H = 0, C = 0], + opcode: [#bin("11110110")/#hex("F6") + `n`], + mem_rw: ([R: `n`],), + mem_addr: ([PC#sub[0]+1],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xF6: + n = read(PC++) + result = A | n + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 0 + flags.C = 0 + ``` +) + +#instruction( + [ + ==== XOR r: Bitwise XOR (register) + + Performs a bitwise XOR operation between the 8-bit A register and the 8-bit register `r`, and stores the result back into the A register. + ], + mnemonic: "XOR r", + length: 1, + duration: 1, + flags: [Z = #flag-update, N = 0, H = 0, C = 0], + opcode: [#bin("10101xxx")/various], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +opcode = read(PC++) +# example: XOR B +if opcode == 0xB8: + result = A ^ B + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 0 + flags.C = 0 + ``` +) + +#instruction( + [ + ==== XOR (HL): Bitwise XOR (indirect HL) + + Performs a bitwise XOR operation between the 8-bit A register and data from the absolute address specified by the 16-bit register HL, and stores the result back into the A register. + ], + mnemonic: "XOR (HL)", + length: 1, + duration: 2, + flags: [Z = #flag-update, N = 0, H = 0, C = 0], + opcode: [#bin("10101110")/#hex("AE")], + mem_rw: ([R: data],), + mem_addr: ([HL],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xBE: + data = read(HL) + result = A ^ data + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 0 + flags.C = 0 + ``` +) + +#instruction( + [ + ==== XOR n: Bitwise XOR (immediate) + + Performs a bitwise XOR operation between the 8-bit A register and immediate data `n`, and stores the result back into the A register. + ], + mnemonic: "XOR n", + length: 1, + duration: 2, + flags: [Z = #flag-update, N = 0, H = 0, C = 0], + opcode: [#bin("11101110")/#hex("EE") + `n`], + mem_rw: ([R: `n`],), + mem_addr: ([PC#sub[0]+1],), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xEE: + n = read(PC++) + result = A ^ n + A = result + flags.Z = 1 if result == 0 else 0 + flags.N = 0 + flags.H = 0 + flags.C = 0 + ``` +) + +#instruction( + [ + ==== CCF: Complement carry flag + + Flips the carry flag, and clears the N and H flags. + ], + mnemonic: "CCF", + length: 1, + duration: 1, + flags: [N = 0, H = 0, C = #flag-update], + opcode: [#bin("00111111")/#hex("3F")], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x3F: + flags.N = 0 + flags.H = 0 + flags.C = ~flags.C + ``` +) + +#instruction( + [ + ==== SCF: Set carry flag + + Sets the carry flag, and clears the N and H flags. + ], + mnemonic: "SCF", + length: 1, + duration: 1, + flags: [N = 0, H = 0, C = 1], + opcode: [#bin("00110111")/#hex("37")], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x37: + flags.N = 0 + flags.H = 0 + flags.C = 1 + ``` +) + +#instruction( + [ + ==== DAA: Decimal adjust accumulator + + TODO + ], + mnemonic: "DAA", + length: 1, + duration: 1, + flags: [Z = #flag-update, H = 0, C = #flag-update], + opcode: [#bin("00100111")/#hex("27")], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +TODO + ``` +) + +#instruction( + [ + ==== CPL: Complement accumulator + + Flips all the bits in the 8-bit A register, and sets the N and H flags. + ], + mnemonic: "CPL", + length: 1, + duration: 1, + flags: [N = 1, H = 1], + opcode: [#bin("00101111")/#hex("2F")], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x2F: + A = ~A + flags.N = 1 + flags.H = 1 + ``` +) + +=== 16-bit arithmetic instructions + +==== INC rr + +TODO + +==== DEC rr + +TODO + +==== ADD HL,rr + +TODO + +==== ADD SP, e + +TODO + +=== Rotate, shift, and bit operation instructions + +==== RLCA + +TODO + +==== RRCA + +TODO + +==== RLA + +TODO + +==== RRA + +TODO + +==== RLC r + +TODO + +==== RLC (HL) + +TODO + +==== RRC r + +TODO + +==== RRC (HL) + +TODO + +==== RL r + +TODO + +==== RL (HL) + +TODO + +==== RR r + +TODO + +==== RR (HL) + +TODO + +==== SLA r + +TODO + +==== SLA (HL) + +TODO + +==== SRA r + +TODO + +==== SRA (HL) + +TODO + +==== SWAP r + +TODO + +==== SWAP (HL) + +TODO + +==== SRL r + +TODO + +==== SRL (HL) + +TODO + +==== BIT b, r + +TODO + +==== BIT b, (HL) + +TODO + +==== RES b, r + +TODO + +==== RES b, (HL) + +TODO + +==== SET b, r + +TODO + +==== SET b, (HL) + +TODO + +=== Control flow instructions + +#instruction( + [ + ==== JP nn: Jump + + Unconditional jump to the absolute address specified by the 16-bit immediate operand `nn`. + ], + mnemonic: "JP nn", + length: 3, + duration: 4, + opcode: [#bin("11000011")/#hex("C3") + LSB of `nn` + MSB of `nn`], + mem_rw: ([R: lsb(`nn`)], [R: msb(`nn`)], "U",), + mem_addr: ([PC#sub[0]+1], [PC#sub[0]+2], "U",), + next_addr: [`nn`], + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xC3: + nn = unsigned_16(lsb=read(PC++), msb=read(PC++)) + PC = nn + ``` +) + +#instruction( + [ + ==== JP HL: Jump to HL + + Unconditional jump to the absolute address specified by the 16-bit register HL. + ], + mnemonic: "JP HL", + length: 1, + duration: 1, + opcode: [#bin("11101001")/#hex("E9")], + mem_rw: (), + mem_addr: (), + next_addr: [HL], + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xE9: + PC = HL + ``` +) + +#warning[ + In some documentation this instruction is written as `JP [HL]`. This is very misleading, since brackets are usually used to indicate a memory read, and this instruction simply copies the value of HL to PC. +] + +#instruction-block( + [ + ==== JP cc, nn: Jump (conditional) + + Conditional jump to the absolute address specified by the 16-bit operand `nn`, depending on the condition `cc`. + + Note that the operand (absolute address) is read even when the condition is false! + ], + [*Opcode*], [#bin("110xx010")/various + LSB of `n` + MSB of `n`], + [*Length*], [3 bytes], + [*Duration*], [3 machine cycles (cc=false), or 4 machine cycles (cc=true)], + [*Flags*], [-], + [*Timing\ _cc=false_*], instruction-timing( + mnemonic: "JP cc, nn", + duration: 3, + mem_rw: ([R: lsb(`nn`)], [R: msb(`nn`)]), + mem_addr: ([PC#sub[0]+1], [PC#sub[0]+2],), + next_addr: [PC#sub[0]+3], + ), + [*Timing\ _cc=true_*], instruction-timing( + mnemonic: "JP cc, nn", + duration: 4, + mem_rw: ([R: lsb(`nn`)], [R: msb(`nn`)], "U",), + mem_addr: ([PC#sub[0]+1], [PC#sub[0]+2], "U",), + next_addr: [`nn`], + ), + [*Pseudocode*], ``` +opcode = read(PC++) +if opcode in [0xC2, 0xD2, 0xCA, 0xDA]: + nn = unsigned_16(lsb=read(PC++), msb=read(PC++)) + if F.check_condition(cc): + PC = nn + ```, +) + +#instruction( + [ + ==== JR e: Relative jump + + Unconditional jump to the relative address specified by the signed 8-bit operand `e`. + ], + mnemonic: "JR e", + length: 2, + duration: 3, + opcode: [#bin("00011000")/#hex("18") + offset `e`], + mem_rw: ([R: `e`], "U",), + mem_addr: ([PC#sub[0]+1], "U",), + next_addr: [PC#sub[0]+2+`e`], + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x18: + e = signed_8(read(PC++)) + PC = PC + e + ``` +) + +#instruction-block( + [ + ==== JR cc, e: Relative jump (conditional) + + Conditional jump to the relative address specified by the signed 8-bit operand `e`, depending on the condition `cc`. + + Note that the operand (relative address offset) is read even when the condition is false! + ], + [*Opcode*], [#bin("001xx000")/various + offset `e`], + [*Length*], [2 bytes], + [*Duration*], [2 machine cycles (cc=false), or 3 machine cycles (cc=true)], + [*Flags*], [-], + [*Timing\ _cc=false_*], instruction-timing( + mnemonic: "JR cc, e", + duration: 2, + mem_rw: ([R: `e`],), + mem_addr: ([PC#sub[0]+1],), + next_addr: [PC#sub[0]+2], + ), + [*Timing\ _cc=true_*], instruction-timing( + mnemonic: "JR cc, e", + duration: 3, + mem_rw: ([R: `e`], "U",), + mem_addr: ([PC#sub[0]+1], "U",), + next_addr: [PC#sub[0]+2+`e`], + ), + [*Pseudocode*], ``` +opcode = read(PC++) +if opcode in [0x20, 0x30, 0x28, 0x38]: + e = signed_8(read(PC++)) + if F.check_condition(cc): + PC = PC + e + ```, +) + +#instruction( + [ + ==== CALL nn: Call function + + Unconditional function call to the absolute address specified by the 16-bit operand `nn`. + ], + mnemonic: "CALL nn", + length: 3, + duration: 6, + opcode: [#bin("11001101")/#hex("CD") + LSB of `nn` + MSB of `nn`], + mem_rw: ([R: lsb(`nn`)], [R: msb(`nn`)], "U", [W: msb(PC#sub[0]+3)], [W: lsb(PC#sub[0]+3)],), + mem_addr: ([PC#sub[0]+1], [PC#sub[0]+2], [SP#sub[0]], [SP#sub[0]-1], [SP#sub[0]-2],), + next_addr: [`nn`], + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xCD: + nn = unsigned_16(lsb=read(PC++), msb=read(PC++)) + SP-- + write(SP--, msb(PC)) + write(SP, lsb(PC)) + PC = nn + ``` +) + +#instruction-block( + [ + ==== CALL cc, nn: Call function (conditional) + + Conditional function call to the absolute address specified by the 16-bit operand `nn`, depending on the condition `cc`. + + Note that the operand (absolute address) is read even when the condition is false! + ], + [*Opcode*], [#bin("110xx100")/various + LSB of `nn` + MSB of `nn`], + [*Length*], [3 bytes], + [*Duration*], [3 machine cycles (cc=false), or 6 machine cycles (cc=true)], + [*Flags*], [-], + [*Timing\ _cc=false_*], instruction-timing( + mnemonic: "CALL cc, nn", + duration: 3, + mem_rw: ([R: lsb(`nn`)], [R: msb(`nn`)],), + mem_addr: ([PC#sub[0]+1], [PC#sub[0]+2],), + next_addr: [PC#sub[0]+3], + ), + [*Timing\ _cc=true_*], instruction-timing( + mnemonic: "CALL cc, nn", + duration: 6, + mem_rw: ([R: lsb(`nn`)], [R: msb(`nn`)], "U", [W: msb(PC#sub[0]+3)], [W: lsb(PC#sub[0]+3)],), + mem_addr: ([PC#sub[0]+1], [PC#sub[0]+2], [SP#sub[0]], [SP#sub[0]-1], [SP#sub[0]-2],), + next_addr: [`nn`], + ), + [*Pseudocode*], ``` +opcode = read(PC++) +if opcode in [0xC4, 0xD4, 0xCC, 0xDC]: + nn = unsigned_16(lsb=read(PC++), msb=read(PC++)) + if F.check_condition(cc): + SP-- + write(SP--, msb(PC)) + write(SP, lsb(PC)) + PC = nn + ```, +) + +#instruction( + [ + ==== RET: Return from function + + Unconditional return from a function. + ], + mnemonic: "RET", + length: 1, + duration: 4, + opcode: [#bin("11001001")/#hex("C9")], + mem_rw: ([R: lsb(PC)], [R: msb(PC)], "U",), + mem_addr: ([SP#sub[0]], [SP#sub[0]+1], "U",), + next_addr: [PC from stack], + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xC9: + PC = unsigned_16(lsb=read(SP++), msb=read(SP++)) + ``` +) + +#instruction-block( + [ + ==== RET cc: Return from function (conditional) + + Conditional return from a function, depending on the condition `cc`. + ], + [*Opcode*], [#bin("110xx000")/various], + [*Length*], [1 bytes], + [*Duration*], [2 machine cycles (cc=false), or 5 machine cycles (cc=true)], + [*Flags*], [-], + [*Timing\ _cc=false_*], instruction-timing( + mnemonic: "RET cc", + duration: 2, + mem_rw: ("U",), + mem_addr: ("U",), + next_addr: [PC#sub[0]+1], + ), + [*Timing\ _cc=true_*], instruction-timing( + mnemonic: "RET cc", + duration: 5, + mem_rw: ("U", [R: lsb(PC)], [R: msb(PC)], "U",), + mem_addr: ("U", [SP#sub[0]], [SP#sub[0]+1], "U",), + next_addr: [PC from stack], + ), + [*Pseudocode*], ``` +opcode = read(PC++) +if opcode in [0xC0, 0xD0, 0xC8, 0xD8]: + if F.check_condition(cc): + PC = unsigned_16(lsb=read(SP++), msb=read(SP++)) + ```, +) + +#instruction( + [ + ==== RETI: Return from interrupt handler + + Unconditional return from a function. Also enables interrupts by setting IME=1. + ], + mnemonic: "RETI", + length: 1, + duration: 4, + opcode: [#bin("11011001")/#hex("D9")], + mem_rw: ([R: lsb(PC)], [R: msb(PC)], "U",), + mem_addr: ([SP#sub[0]], [SP#sub[0]+1], "U",), + next_addr: [PC from stack], + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xD9: + PC = unsigned_16(lsb=read(SP++), msb=read(SP++)) + IME = 1 + ``` +) + +#instruction( + [ + ==== RST n: Restart / Call function (implied) + + Unconditional function call to the absolute fixed address defined by the opcode. + ], + mnemonic: "RST n", + length: 1, + duration: 4, + opcode: [#bin("11xxx111")/various], + mem_rw: ("U", [W: msb(PC#sub[0]+1)], [W: lsb(PC#sub[0]+1)],), + mem_addr: ([SP#sub[0]], [SP#sub[0]-1], [SP#sub[0]-2],), + next_addr: [`n`], + pseudocode: ``` +opcode = read(PC++) +if opcode in [0xC7, 0xD7, 0xE7, 0xF7, 0xCF, 0xDF, 0xEF, 0xFF]: + n = rst_address(opcode) + SP-- + write(SP--, msb(PC)) + write(SP, lsb(PC)) + PC = unsigned_16(lsb=n, msb=0x00) + ``` +) + +=== Miscellaneous instructions + +==== HALT: Halt system clock + +TODO + +==== STOP: Stop system and main clocks + +TODO + +#instruction( + [ + ==== DI: Disable interrupts + + Disables interrupt handling by setting IME=0 and cancelling any scheduled effects of the EI instruction if any. + ], + mnemonic: "DI", + length: 1, + duration: 1, + opcode: [#bin("11110011")/#hex("F3")], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0xF3: + IME = 0 + ``` +) + +#instruction-block( + [ + ==== EI: Enable interrupts + + Schedules interrupt handling to be enabled after the next machine cycle. + ], + [*Opcode*], [#bin("11111011")/#hex("FB")], + [*Length*], [1 byte], + [*Duration*], [2 machine cycles (cc=false), or 5 machine cycles (cc=true)], + [*Flags*], [-], + [*Timing*], instruction-timing( + mnemonic: "EI", + duration: 1, + mem_rw: (), + mem_addr: (), + next_addr: [PC#sub[0]+1], + ), + [*Pseudocode*], ``` +opcode = read(PC++) +if opcode == 0xFB: + IME_scheduled = true + ```, +) + +#instruction( + [ + ==== NOP: No operation + + No operation. This instruction doesn't do anything, but can be used to add a delay of one machine cycle and increment PC by one. + ], + mnemonic: "NOP", + length: 1, + duration: 1, + opcode: [#bin("00000000")/#hex("00")], + mem_rw: (), + mem_addr: (), + pseudocode: ``` +opcode = read(PC++) +if opcode == 0x00: + // nothing + ``` +) diff --git a/chapter/cpu/timing.tex b/chapter/cpu/timing.tex deleted file mode 100644 index 9cd1bc6..0000000 --- a/chapter/cpu/timing.tex +++ /dev/null @@ -1,84 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{CPU core timing} - -\section{Fetch/execute overlap} - -Sharp SM83 uses a microprocessor design technique known as \emph{fetch/execute -overlap} to improve CPU performance by doing opcode fetches in parallel with -instruction execution whenever possible. Since the CPU can only perform one -memory access per M-cycle, it is worth it to try to do memory operations as -soon as possible. Also, when doing a memory read, the CPU cannot use the data -during the same M-cycle so the true minimum effective duration of instructions -is 2 machine cycles, not 1 machine cycle. - -Every instruction needs one machine cycle for the fetch stage, and at least one -machine cycle for the decode/execute stage. However, the fetch stage of an -instruction always overlaps with the last machine cycle of the execute stage of -the previous instruction. The overlapping execute stage cycle may still do some -work (e.g. ALU operation and/or register writeback) but memory access is -reserved for the fetch stage of the next instruction. - -Since all instructions effectively last one machine cycle longer, fetch/execute -overlap is usually ignored in documentation intended for programmers. It is -much easier to think of a program as a sequence of non-overlapping instructions -and consider only the execute stages when calculating instruction durations. -However, when emulating a SM83 CPU core, understanding and emulating the -overlap can be useful. - -\begin{warning} - Sharp SM831x is a family of single-chip SoCs from Sharp that use the SM83 CPU - core, and their datasheet \cite{sm831x} includes a description of - fetch/execute overlap. However, the description is not completely correct and - can in fact be misleading. - - For example, the timing diagram includes an instruction that does not involve - opcode fetch at all, and memory operations for two instructions are shown to - happen at the same time, which is not possible. -\end{warning} - -\subsection{Fetch/execute overlap timing example} - -Let's assume the CPU is executing a program that starts from the address -\hex{1000} and contains the following instructions:\\ -{ - \ttfamily - \hex{1000}:\colorbox{blue!30}{INC A} -}\\ -{ - \ttfamily - \hex{1001}:\colorbox{yellow!30}{LDH (n), A} -}\\ -{ - \ttfamily - \hex{1003}:\colorbox{green!30}{RST \hex{08}} -}\\ -{ - \ttfamily - \hex{0008}:\colorbox{red!30}{NOP} -} - -The following timing diagram shows all memory operations done by the CPU, and -the fetch and execute stages of each instruction: - -\begin{figure}[H] - \centering - \begin{tikztimingtable}[timing/wscale=0.6] - CLK 4MHz & L 10{8{C}} C \\ - PHI 1MHz & L 20{4C} C \\ - Mem R/W & X ; [fill=blue!30] 8D{R: opcode} ; [fill=yellow!30] 8D{R: opcode} 8D{R: n} 8D{W: A} ; [fill=green!30] 8D{R: opcode} ; 8X ; [fill=green!30] 8D{W: msb(PC)} 8D{W: lsb(PC)} ; [fill=red!30] 8D{R: opcode} ; [opacity=0.4] 8D{R: opcode} X \\ - Before \texttt{INC A} & ; [opacity=0.4] 9D{execute} ; 73X \\ - \texttt{INC A} & X ; [fill=blue!30] 8D{M1: fetch} 8D{M2: execute} ; 65X \\ - \texttt{LDH (n), A} & X 8X ; [fill=yellow!30] 8D{M1: fetch} 24D{M2-4: execute} ; 41X \\ - \texttt{RST \hex{08}} & X 32X ; [fill=green!30] 8D{M1: fetch} 32D{M2-5: execute} ; 9X \\ - \texttt{NOP} & X 64X ; [fill=red!30] 8D{M1: fetch} 8D{M2: execute} ; X \\ - After \texttt{NOP} & X 72X ; [opacity=0.4] 9D{M1: fetch} \\ - \end{tikztimingtable} -\caption{Fetch/execute overlap example} -\end{figure} - -\end{document} diff --git a/chapter/cpu/timing.typ b/chapter/cpu/timing.typ new file mode 100644 index 0000000..049a29b --- /dev/null +++ b/chapter/cpu/timing.typ @@ -0,0 +1,123 @@ +#import "../../common.typ": * +#import "../../timing.typ" + +== CPU core timing + +=== Fetch/execute overlap + +Sharp SM83 uses a microprocessor design technique known as _fetch/execute overlap_ to improve CPU performance by doing opcode fetches in parallel with instruction execution whenever possible. Since the CPU can only perform one memory access per M-cycle, it is worth it to try to do memory operations as soon as possible. Also, when doing a memory read, the CPU cannot use the data during the same M-cycle so the true minimum effective duration of instructions is 2 machine cycles, not 1 machine cycle. + +Every instruction needs one machine cycle for the fetch stage, and at least one machine cycle for the decode/execute stage. However, the fetch stage of an instruction always overlaps with the last machine cycle of the execute stage of the previous instruction. The overlapping execute stage cycle may still do some work (e.g. ALU operation and/or register writeback) but memory access is reserved for the fetch stage of the next instruction. + +Since all instructions effectively last one machine cycle longer, fetch/execute overlap is usually ignored in documentation intended for programmers. It is much easier to think of a program as a sequence of non-overlapping instructions and consider only the execute stages when calculating instruction durations. However, when emulating a SM83 CPU core, understanding and emulating the overlap can be useful. + +#warning[ + Sharp SM831x is a family of single-chip SoCs from Sharp that use the SM83 CPU core, and their datasheet @sm831x includes a description of fetch/execute overlap. However, the description is not completely correct and can in fact be misleading. + + For example, the timing diagram includes an instruction that does not involve opcode fetch at all, and memory operations for two instructions are shown to happen at the same time, which is not possible. +] + +==== Fetch/execute overlap timing example + +Let's assume the CPU is executing a program that starts from the address #hex("1000") and contains the following instructions: + +#let colors = ( + inc: rgb("#b3b3ff"), + ldh: rgb("#fffcb3"), + rst: rgb("#b3ffb3"), + nop: rgb("#ffb3b3"), +) + +#monotext[ + #tablex( + columns: 2, + align: left + horizon, + stroke: none, + hex("1000"), cellx(fill: colors.inc)[INC A], + hex("1001"), cellx(fill: colors.ldh)[LDH (n), A], + hex("1003"), cellx(fill: colors.rst)[RST #hex("08")], + hex("0008"), cellx(fill: colors.nop)[NOP] + ) +] + +The following timing diagram shows all memory operations done by the CPU, and +the fetch and execute stages of each instruction: + +#figure({ + import timing: * + diagram( + w_scale: 0.6, + (label: "CLK 4 MiHz", wave: ( + l(1), + ..range(80).map(_ => c(1)), + c(1), + )), + (label: "PHI 1 MiHz", wave: ( + l(1), + ..range(10).map(_ => (c(4), c(4))).flatten(), + c(1), + )), + (label: "Mem R/W", wave: ( + x(1), + d(8, [R: opcode], fill: colors.inc), + d(8, [R: opcode], fill: colors.ldh), + d(8, [R: n], fill: colors.ldh), + d(8, [W: A], fill: colors.ldh), + d(8, [R: opcode], fill: colors.rst), + x(8), + d(8, [W: msb(PC)], fill: colors.rst), + d(8, [W: lsb(PC)], fill: colors.rst), + d(8, [R: opcode], fill: colors.nop), + d(8, [R: opcode], opacity: 60%), + x(1, opacity: 60%), + )), + (label: "Mem addr", wave: ( + x(1), + d(8, hex("1000"), fill: colors.inc), + d(8, hex("1001"), fill: colors.ldh), + d(8, hex("1002"), fill: colors.ldh), + d(8, monotext[#hex("FF00")+n], fill: colors.ldh), + d(8, hex("1003"), fill: colors.rst), + x(8), + d(8, monotext[SP-1], fill: colors.rst), + d(8, monotext[SP-2], fill: colors.rst), + d(8, hex("0008"), fill: colors.nop), + d(8, hex("0009"), opacity: 60%), + x(1, opacity: 60%), + )), + ([Before #monotext[INC A]], wave: ( + d(9, [execute], opacity: 60% ), + x(73), + )), + (label: monotext[INC A], wave: ( + x(1), + d(8, [M1: fetch], fill: colors.inc), + d(8, [M2: execute], fill: colors.inc), + x(65), + )), + (label: monotext[LDH (n), A], wave: ( + x(9), + d(8, [M1: fetch], fill: colors.ldh ), + d(24, [M2-4: execute], fill: colors.ldh ), + x(41), + )), + (label: monotext[RST #hex("08")], wave: ( + x(33), + d(8, [M1: fetch], fill: colors.rst), + d(32, [M2-5: execute], fill: colors.rst), + x(9), + )), + (label: monotext[NOP], wave: ( + x(65), + d(8, [M1: fetch], fill: colors.nop), + d(8, [M2: execute], fill: colors.nop), + x(1), + )), + ([After #monotext[NOP]], wave: ( + x(73), + d(8, [M1: fetch], opacity: 60%), + x(1, opacity: 60%), + )), + )}, + caption: "Fetch/execute overlap example" +) diff --git a/chapter/peripherals/boot-rom.tex b/chapter/peripherals/boot-rom.tex deleted file mode 100644 index 21d0543..0000000 --- a/chapter/peripherals/boot-rom.tex +++ /dev/null @@ -1,122 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{Boot ROM} - -The Game Boy SoC includes a small embedded boot ROM, which can be mapped to the -\hexrange{0000}{00FF} memory area. While mapped, all reads from this area are -handled by the boot ROM instead of the external cartridge, and all writes to -this area are ignored and cannot be seen by external hardware (e.g. the -cartridge MBC). - -The boot ROM is enabled by default, so when the system exits the reset state -and the CPU starts execution from address \hex{0000}, it executes the boot ROM -instead of instructions from the cartridge ROM. The boot ROM is responsible for -showing the initial logo, and checking that a valid cartridge is inserted into -the system. If the cartridge is valid, the boot ROM unmaps itself before -execution of the cartridge ROM starts at \hex{0100}. The cartridge ROM has no -chance of executing any instructions before the boot ROM is unmapped, which -prevents the boot ROM from being read byte by byte in normal conditions. - -\begin{warning} - Don't confuse the boot ROM with the additional SNES ROM in SGB/SGB2 that is - executed by the SNES CPU. -\end{warning} - -\begin{register}[H] - \caption{\hex{FF50} - BOOT - Boot ROM lock register} - { - \ttfamily - \begin{tabularx}{\linewidth}{|C|C|C|C|C|C|C|C|} - \hline - U-1 & U-1 & U-1 & U-1 & U-1 & U-1 & U-1 & R/W-0 \\ - \hline - \cellcolor{LightGray} - & \cellcolor{LightGray} - & \cellcolor{LightGray} - & \cellcolor{LightGray} - & \cellcolor{LightGray} - & \cellcolor{LightGray} - & \cellcolor{LightGray} - & BOOT\_OFF \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } - - \begin{description}[leftmargin=5em, style=nextline] - \item[bit 7-1] - \textbf{Unimplemented}: Read as \bit{1} - \item[bit 0] - \textbf{BOOT\_OFF}: Boot ROM lock bit\\ - \bin{1}= Boot ROM is disabled and \hexrange{0000}{00FF} works normally.\\ - \bin{0}= Boot ROM is active and intercepts accesses to \hexrange{0000}{00FF}. - - \bigskip - - BOOT\_OFF can only transition from \bin{0} to \bin{1}, so once \bin{1} - has been written, the boot ROM is permanently disabled until the next - system reset. Writing \bin{0} when BOOT\_OFF is \bin{0} has no effect and - doesn't lock the boot ROM. - \end{description} -\end{register} - -The 1-bit BOOT register controls mapping of the boot ROM. Once \bit{1} has been -written to it to unmap the boot ROM, it can only be mapped again by resetting -the system. - -\section{Boot ROM types} - -\begin{table}[H] - \caption{Summary of boot ROM file hashes} - \centering - \small - \begin{tabular}{|l|l|l|l|} - \hline - Type & CRC32 & MD5 & SHA1 \\ - \hline - DMG & \texttt{59c8598e} & \texttt{32fbbd84168d3482956eb3c5051637f5} & \texttt{4ed31ec6b0b175bb109c0eb5fd3d193da823339f} \\ - \hline - MGB & \texttt{e6920754} & \texttt{71a378e71ff30b2d8a1f02bf5c7896aa} & \texttt{4e68f9da03c310e84c523654b9026e51f26ce7f0} \\ - \hline - SGB & \texttt{ec8a83b9} & \texttt{d574d4f9c12f305074798f54c091a8b4} & \texttt{aa2f50a77dfb4823da96ba99309085a3c6278515} \\ - \hline - SGB2 & \texttt{53d0dd63} & \texttt{e0430bca9925fb9882148fd2dc2418c1} & \texttt{93407ea10d2f30ab96a314d8eca44fe160aea734} \\ - \hline - DMG0 & \texttt{c2f5cc97} & \texttt{a8f84a0ac44da5d3f0ee19f9cea80a8c} & \texttt{8bd501e31921e9601788316dbd3ce9833a97bcbc} \\ - \hline - \end{tabular} -\end{table} - -\subsection{DMG boot ROM} - -The most common boot ROM is the DMG boot ROM used in almost all original Game -Boy units. If a valid cartridge is inserted, the boot ROM scrolls a logo to the -center of the screen, and plays a "di-ding" sound recognizable by most people -who have used Game Boy consoles. - -This boot ROM was originally dumped by neviksti in 2003 by decapping the Game -Boy SoC and visually inspecting every single bit. - -\subsection{MGB boot ROM} - -This boot ROM was originally dumped by Bennvenn in 2014 by using a simple clock -glitching method that only requires one wire. - -\subsection{SGB boot ROM} - -This boot ROM was originally dumped by Costis Sideris in 2009 by using an -FPGA-based clock glitching method \cite{costis_sgb}. - -\subsection{SGB2 boot ROM} - -This boot ROM was originally dumped by gekkio in 2015 by using a Teensy 3.1 --based clock glitching method \cite{gekkio_sgb2}. - -\subsection{Early DMG boot ROM} - -Very early original Game Boy units released in Japan (often called "DMG0") -included the launch version "DMG-CPU" SoC chip, which used a different boot ROM -than later units. - -This boot ROM was originally dumped by gekkio in 2016 by using a clock -glitching method invented by BennVenn. - -\end{document} diff --git a/chapter/peripherals/boot-rom.typ b/chapter/peripherals/boot-rom.typ new file mode 100644 index 0000000..86f0d91 --- /dev/null +++ b/chapter/peripherals/boot-rom.typ @@ -0,0 +1,80 @@ +#import "../../common.typ": * + +== Boot ROM + +The Game Boy SoC includes a small embedded boot ROM, which can be mapped to the #hex-range("0000", "00FF") memory area. While mapped, all reads from this area are handled by the boot ROM instead of the external cartridge, and all writes to this area are ignored and cannot be seen by external hardware (e.g. the cartridge MBC). + +The boot ROM is enabled by default, so when the system exits the reset state and the CPU starts execution from address #hex("0000"), it executes the boot ROM instead of instructions from the cartridge ROM. The boot ROM is responsible for showing the initial logo, and checking that a valid cartridge is inserted into the system. If the cartridge is valid, the boot ROM unmaps itself before execution of the cartridge ROM starts at #hex("0100"). The cartridge ROM has no chance of executing any instructions before the boot ROM is unmapped, which prevents the boot ROM from being read byte by byte in normal conditions. + +#warning[ + Don't confuse the boot ROM with the additional SNES ROM in SGB/SGB2 that is + executed by the SNES CPU. +] + +#reg-figure( + caption: [#hex("FF50") - BOOT - Boot ROM lock register] +)[ + #reg-table( + [U], [U], [U], [U], [U], [U], [U], [R/W-0], + unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), [BOOT_OFF], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) + #set align(left) + #grid( + columns: (auto, 1fr), + gutter: 1em, + [*bit 7-1*], [*Unimplemented*: Ignored during writes, reads are undefined], + [*bit 0*], [ + *BOOT_OFF*: Boot ROM lock bit\ + #bin("1") = Boot ROM is disabled and #hex-range("0000", "00FF") works normally.\ + #bin("0") = Boot ROM is active and intercepts accesses to #hex-range("0000", "00FF"). + + #v(1em) + + BOOT_OFF can only transition from #bin("0") to #bin("1"), so once #bin("1") has been written, the boot ROM is permanently disabled until the next system reset. Writing #bin("0") when BOOT_OFF is #bin("0") has no effect and doesn't lock the boot ROM. + ] + ) +] + +The 1-bit BOOT register controls mapping of the boot ROM. Once #bin("1") has been written to it to unmap the boot ROM, it can only be mapped again by resetting the system. + +=== Boot ROM types + +#show raw: set text(7pt) +#figure( + table( + columns: 4, + align: left + horizon, + [Type], [CRC32], [MD5], [SHA1], + [DMG], `59c8598e`, `32fbbd84168d3482956eb3c5051637f5`, `4ed31ec6b0b175bb109c0eb5fd3d193da823339f`, + [MGB], `e6920754`, `71a378e71ff30b2d8a1f02bf5c7896aa`, `4e68f9da03c310e84c523654b9026e51f26ce7f0`, + [SGB], `ec8a83b9`, `d574d4f9c12f305074798f54c091a8b4`, `aa2f50a77dfb4823da96ba99309085a3c6278515`, + [SGB2], `53d0dd63`, `e0430bca9925fb9882148fd2dc2418c1`, `93407ea10d2f30ab96a314d8eca44fe160aea734`, + [DMG0], `c2f5cc97`, `a8f84a0ac44da5d3f0ee19f9cea80a8c`, `8bd501e31921e9601788316dbd3ce9833a97bcbc`, + ), + caption: "Summary of boot ROM file hashes" +) + +==== DMG boot ROM + +The most common boot ROM is the DMG boot ROM used in almost all original Game Boy units. If a valid cartridge is inserted, the boot ROM scrolls a logo to the center of the screen, and plays a "di-ding" sound recognizable by most people who have used Game Boy consoles. + +This boot ROM was originally dumped by neviksti in 2003 by decapping the Game Boy SoC and visually inspecting every single bit. + +==== MGB boot ROM + +This boot ROM was originally dumped by Bennvenn in 2014 by using a simple clock glitching method that only requires one wire. + +==== SGB boot ROM + +This boot ROM was originally dumped by Costis Sideris in 2009 by using an FPGA-based clock glitching method @costis_sgb. + +==== SGB2 boot ROM + +This boot ROM was originally dumped by gekkio in 2015 by using a Teensy 3.1 -based clock glitching method @gekkio_sgb2. + +==== Early DMG boot ROM ("DMG0") + +Very early original Game Boy units released in Japan (often called "DMG0") included the launch version "DMG-CPU" SoC chip, which used a different boot ROM than later units. + +This boot ROM was originally dumped by gekkio in 2016 by using a clock glitching method invented by BennVenn. diff --git a/chapter/peripherals/dma.tex b/chapter/peripherals/dma.tex deleted file mode 100644 index 76f57ad..0000000 --- a/chapter/peripherals/dma.tex +++ /dev/null @@ -1,112 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{DMA (Direct Memory Access)} - -\section{Object Attribute Memory (OAM) DMA} - -OAM DMA is a high-throughput mechanism for copying data to the OAM area (a.k.a. -Object Attribute Memory, a.k.a. sprite memory). It can copy one byte per -machine cycle without involving the CPU at all, which is much faster than the -fastest possible \texttt{memcpy} routine that can be written with the SM83 -instruction set. However, a transfer cannot be cancelled and the transfer -length cannot be controlled, so the DMA transfer always updates the entire OAM -area (= 160 bytes) even if you actually want to just update the first couple of -bytes. - -The Game Boy CPU chip contains a DMA controller that coordinates transfers -between a \emph{source area} and the \emph{OAM area} independently of the CPU. -While a transfer is in progress, it takes control of the source bus and the OAM -area, so some precaution is needed with memory accesses (including instruction -fetches) to avoid OAM DMA bus conflicts. OAM DMA uses a different address -decoding scheme than normal memory accesses, so the source bus is always either -the external bus or the video RAM bus, and the contents normally visible to the -CPU in the \hexrange{FE00}{FFFF} address range cannot be used as a source for -OAM DMA transfers. - -The upper 8 bits of the OAM DMA source address are stored in the DMA register, -while the lower 8 bits used by both the source and target address are stored in -the DMA controller and are not accessible directly. A transfer always begins -with \hex{00} in the lower bits and copies exactly 160 bytes, so the lower bits -are never in the \hexrange{A0}{FF} range. - -Writing to the DMA register updates the upper bits of the DMA source address -and also triggers an OAM DMA transfer request, although the DMA transfer does -not begin immediately. - -\begin{register}[H] - \caption{\hex{FF46} - DMA - OAM DMA control register} - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - R/W-x & R/W-x & R/W-x & R/W-x & R/W-x & R/W-x & R/W-x & R/W-x \\ - \hline - \multicolumn{8}{|c|}{DMA<7:0>} \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } - - \begin{description}[leftmargin=5em, style=nextline] - \item[bit 0] - \textbf{DMA<7:0>}: OAM DMA source address\\ - Specifies the top 8 bits of the OAM DMA source address. - - Writing to this register requests an OAM DMA transfer, but it's just a - request and the actual DMA transfer starts with a delay. - - Reading this register returns the value that was previously written to - the register. The stored value is not cleared on reset, so the initial - value before the first write is unknown and should not be relied on. - \end{description} -\end{register} - -\begin{warning} - Avoid writing \hexrange{E0}{FF} to the DMA register, because some poorly -designed flash carts can trigger bus conflicts or other dangerous behaviour. -\end{warning} - -\subsection{OAM DMA address decoding} - -The OAM DMA controller uses a simplified address decoding scheme, which leads -to some addresses being unusable as source addresses. Unlike normal memory -accesses, OAM DMA transfers interpret all accesses in the \hexrange{A000}{FFFF} -range as external RAM transfers. For example, if the OAM DMA wants to read -\hex{FF00}, it will output \hex{FF00} on the external address bus and will -assert the external RAM chip select signal. The P1 register which is normally -at \hex{FF00} is not involved at all, because OAM DMA address decoding only -uses the external bus and the video RAM bus. Instead, the resulting behaviour -depends on several factors, including the connected cartridge. Some flash carts -are not prepared for this unexpected scenario, and a bus conflict or worse -behaviour can happen. - -\begin{table}[H] - \caption{OAM DMA address decoding scheme} - \rmfamily - \begin{tabularx}{\linewidth}{|L|L|L|} - \hline - DMA register value & Used bus & Asserted chip select signal \\ - \hline - \hexrange{00}{7F} & external bus & external ROM (A15) \\ - \hline - \hexrange{80}{9F} & video RAM bus & video RAM (MCS) \\ - \hline - \hexrange{A0}{FF} & external bus & external RAM (CS) \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} -\end{table} - -\subsection{OAM DMA transfer timing} - -TODO - -\subsection{OAM DMA bus conflicts} - -TODO - -\end{document} diff --git a/chapter/peripherals/dma.typ b/chapter/peripherals/dma.typ new file mode 100644 index 0000000..dc1d967 --- /dev/null +++ b/chapter/peripherals/dma.typ @@ -0,0 +1,64 @@ +#import "../../common.typ": * + +== DMA (Direct Memory Access) + +=== Object Attribute Memory (OAM) DMA + +OAM DMA is a high-throughput mechanism for copying data to the OAM area (a.k.a. Object Attribute Memory, a.k.a. sprite memory). It can copy one byte per machine cycle without involving the CPU at all, which is much faster than the fastest possible `memcpy` routine that can be written with the SM83 instruction set. However, a transfer cannot be cancelled and the transfer length cannot be controlled, so the DMA transfer always updates the entire OAM area (= 160 bytes) even if you actually want to just update the first couple of bytes. + +The Game Boy CPU chip contains a DMA controller that coordinates transfers between a *source area* and the *OAM area* independently of the CPU. While a transfer is in progress, it takes control of the source bus and the OAM area, so some precaution is needed with memory accesses (including instruction fetches) to avoid OAM DMA bus conflicts. OAM DMA uses a different address decoding scheme than normal memory accesses, so the source bus is always either the external bus or the video RAM bus, and the contents normally visible to the CPU in the #hex-range("FE00", "FFFF") address range cannot be used as a source for OAM DMA transfers. + +The upper 8 bits of the OAM DMA source address are stored in the DMA register, while the lower 8 bits used by both the source and target address are stored in the DMA controller and are not accessible directly. A transfer always begins with #hex("00") in the lower bits and copies exactly 160 bytes, so the lower bits are never in the #hex-range("A0", "FF") range. + +Writing to the DMA register updates the upper bits of the DMA source address and also triggers an OAM DMA transfer request, although the DMA transfer does not begin immediately. + +#reg-figure( + caption: [#hex("FF46") - DMA - OAM DMA control register] +)[ + #reg-table( + [R/W-x], [R/W-x], [R/W-x], [R/W-x], [R/W-x], [R/W-x], [R/W-x], [R/W-x], + colspanx(8)[DMA\<7:0\>], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) + #set align(left) + #grid( + columns: (auto, 1fr), + gutter: 1em, + [*bit 7-0*], [ + *DMA\<7:0\>*: OAM DMA source address\ + Specifies the top 8 bits of the OAM DMA source address. + + Writing to this register requests an OAM DMA transfer, but it's just a request and the actual DMA transfer starts with a delay. + +Reading this register returns the value that was previously written to the register. The stored value is not cleared on reset, so the initial value before the first write is unknown and should not be relied on. + ], + ) +] + +#warning[ + Avoid writing #hex-range("E0", "FF") to the DMA register, because some poorly designed flash carts can trigger bus conflicts or other dangerous behaviour. +] + +==== OAM DMA address decoding + +The OAM DMA controller uses a simplified address decoding scheme, which leads to some addresses being unusable as source addresses. Unlike normal memory accesses, OAM DMA transfers interpret all accesses in the #hex-range("A000", "FFFF") range as external RAM transfers. For example, if the OAM DMA wants to read #hex("FF00"), it will output #hex("FF00") on the external address bus and will assert the external RAM chip select signal. The P1 register which is normally at #hex("FF00") is not involved at all, because OAM DMA address decoding only uses the external bus and the video RAM bus. Instead, the resulting behaviour depends on several factors, including the connected cartridge. Some flash carts are not prepared for this unexpected scenario, and a bus conflict or worse behaviour can happen. + +#figure( + table( + columns: 3, + align: left + horizon, + [DMA register value], [Used bus], [Asserted chip select signal], + hex-range("00", "7F"), [external bus], [external ROM (A15)], + hex-range("80", "9F"), [video RAM bus], [video RAM (MCS)], + hex-range("A0", "FF"), [external bus], [external RAM (CS)], + ), + caption: "OAM DMA address decoding scheme" +) + +==== OAM DMA transfer timing + +TODO + +==== OAM DMA bus conflicts + +TODO diff --git a/chapter/peripherals/p1.tex b/chapter/peripherals/p1.tex deleted file mode 100644 index 0a4ce80..0000000 --- a/chapter/peripherals/p1.tex +++ /dev/null @@ -1,42 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{Port P1 (Joypad, Super Game Boy communication)} - -\begin{register}[H] - \caption{\hex{FF00} - P1 - Joypad/Super Game Boy communication register} - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - U-1 & U-1 & W-0 & W-0 & R-x & R-x & R-x & R-x \\ - \hline - \cellcolor{LightGray} - & \cellcolor{LightGray} - & P15 & P14 & P13 & P12 & P11 & P10 \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } - - \begin{description}[leftmargin=5em, style=nextline] - \item[bit 7-6] - \textbf{Unimplemented}: Read as \bit{1} - \item[bit 5] - \textbf{P15}: - \item[bit 4] - \textbf{P14}: - \item[bit 3] - \textbf{P13}: - \item[bit 2] - \textbf{P12}: - \item[bit 1] - \textbf{P11}: - \item[bit 0] - \textbf{P10}: - \end{description} -\end{register} - -\end{document} diff --git a/chapter/peripherals/p1.typ b/chapter/peripherals/p1.typ new file mode 100644 index 0000000..66761dd --- /dev/null +++ b/chapter/peripherals/p1.typ @@ -0,0 +1,25 @@ +#import "../../common.typ": * + +== Port P1 (Joypad, Super Game Boy communication) + +#reg-figure( + caption: [#hex("FF00") - P1 - Joypad/Super Game Boy communication register] +)[ + #reg-table( + [U], [U], [W-0], [W-0], [R-x], [R-x], [R-x], [R-x], + unimpl-bit(), unimpl-bit(), [P15], [P14], [P13], [P12], [P11], [P10], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) + #set align(left) + #grid( + columns: (auto, 1fr), + gutter: 1em, + [*bit 7-6*], [*Unimplemented*: Ignored during writes, reads are undefined], + [*bit 5*], [*P15*], + [*bit 4*], [*P14*], + [*bit 3*], [*P13*], + [*bit 2*], [*P12*], + [*bit 1*], [*P11*], + [*bit 0*], [*P10*], + ) +] diff --git a/chapter/peripherals/ppu.tex b/chapter/peripherals/ppu.tex deleted file mode 100644 index bfabc66..0000000 --- a/chapter/peripherals/ppu.tex +++ /dev/null @@ -1,105 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{PPU (Picture Processing Unit)} - -\begin{register}[H] - \caption{\hex{FF40} - LCDC - PPU control register} - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 \\ - \hline - LCD\_EN & WIN\_MAP & WIN\_EN & TILE\_SEL & BG\_MAP & OBJ\_SIZE & OBJ\_EN & BG\_EN \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } -\end{register} - -\begin{register}[H] - \caption{\hex{FF41} - STAT - PPU status register} - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - U-1 & R/W-0 & R/W-0 & R/W-0 & R/W-0 & R-0 & R-0 & R-0 \\ - \hline - \cellcolor{LightGray} - & INTR\_LYC & INTR\_M2 & INTR\_M1 & INTR\_M0 & LYC\_STAT & \multicolumn{2}{c|}{LCD\_MODE<1:0>} \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } -\end{register} - -\begin{register}[H] - \caption{\hex{FF42} - SCY - Vertical scroll register} - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 \\ - \hline - \multicolumn{8}{|c|}{SCY<7:0>} \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } -\end{register} - -\begin{register}[H] - \caption{\hex{FF43} - SCX - Horizontal scroll register} - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 \\ - \hline - \multicolumn{8}{|c|}{SCX<7:0>} \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } -\end{register} - -\begin{register}[H] - \caption{\hex{FF44} - LY - Scanline register} - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - R-0 & R-0 & R-0 & R-0 & R-0 & R-0 & R-0 & R-0 \\ - \hline - \multicolumn{8}{|c|}{LY<7:0>} \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } -\end{register} - -\begin{register}[H] - \caption{\hex{FF45} - LYC - Scanline compare register} - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 \\ - \hline - \multicolumn{8}{|c|}{LYC<7:0>} \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } -\end{register} - -\end{document} diff --git a/chapter/peripherals/ppu.typ b/chapter/peripherals/ppu.typ new file mode 100644 index 0000000..1b1510e --- /dev/null +++ b/chapter/peripherals/ppu.typ @@ -0,0 +1,63 @@ +#import "../../common.typ": * + +== PPU (Picture Processing Unit) + +#reg-figure( + caption: [#hex("FF40") - LCDC - PPU control register] +)[ + #reg-table( + [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], + [LCD_EN], [WIN_MAP], [WIN_EN], [TILE_SEL], [BG_MAP], [OBJ_SIZE], [OBJ_EN], [BG_EN], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) +] + +#reg-figure( + caption: [#hex("FF41") - STAT - PPU status register] +)[ + #reg-table( + [U], [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R-0], [R-0], [R-0], + unimpl-bit(), [INTR_LYC], [INTR_M2], [INTR_M1], [INTR_M0], [LYC_STAT], colspanx(2)[LCD_MODE\<1:0\>], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) +] + +#reg-figure( + caption: [#hex("FF42") - SCY - Vertical scroll register] +)[ + #reg-table( + [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], + colspanx(8)[SCY\<7:0\>], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) +] + +#reg-figure( + caption: [#hex("FF43") - SCX - Horizontal scroll register] +)[ + #reg-table( + [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], + colspanx(8)[SCX\<7:0\>], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) +] + +#reg-figure( + caption: [#hex("FF44") - LY - Scanline register] +)[ + #reg-table( + [R-0], [R-0], [R-0], [R-0], [R-0], [R-0], [R-0], [R-0], + colspanx(8)[LY\<7:0\>], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) +] + +#reg-figure( + caption: [#hex("FF45") - LYC - Scanline compare register] +)[ + #reg-table( + [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], + colspanx(8)[LYC\<7:0\>], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) +] diff --git a/chapter/peripherals/serial.tex b/chapter/peripherals/serial.tex deleted file mode 100644 index 6aeac31..0000000 --- a/chapter/peripherals/serial.tex +++ /dev/null @@ -1,55 +0,0 @@ -%!TEX root = ../../gbctr.tex -%!TEX program = lualatex -\providecommand{\main}{../..} -\documentclass[\main/gbctr.tex]{subfiles} -\begin{document} - -\chapter{Serial communication} - -\begin{register}[H] - \caption{\hex{FF01} - SB - Serial data register} - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 & R/W-0 \\ - \hline - \multicolumn{8}{|c|}{SB<7:0>} \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } - - \begin{description}[leftmargin=5em, style=nextline] - \item[bit 7-0] - \textbf{SB<7:0>}: Serial data - \end{description} -\end{register} - -\begin{register}[H] - \caption{\hex{FF02} - SC - Serial control register} - { - \ttfamily - \begin{tabularx}{\linewidth}{|X|X|X|X|X|X|X|X|} - \hline - R/W-0 & U-1 & U-1 & U-1 & U-1 & U-1 & U-1 & R/W-0 \\ - \hline - SIO\_EN & \cellcolor{LightGray} - & \cellcolor{LightGray} - & \cellcolor{LightGray} - & \cellcolor{LightGray} - & \cellcolor{LightGray} - & \cellcolor{LightGray} - & SIO\_CLK \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } - - \begin{description}[leftmargin=5em, style=nextline] - \item[bit 7] - \textbf{SIO\_EN}: - \item[bit 6-1] - \textbf{Unimplemented}: Read as \bit{1} - \item[bit 0] - \textbf{SIO\_CLK}: - \end{description} -\end{register} - -\end{document} diff --git a/chapter/peripherals/serial.typ b/chapter/peripherals/serial.typ new file mode 100644 index 0000000..5d32f28 --- /dev/null +++ b/chapter/peripherals/serial.typ @@ -0,0 +1,37 @@ +#import "../../common.typ": * + +== Serial communication + +#reg-figure( + caption: [#hex("FF01") - SB - Serial data register] +)[ + #reg-table( + [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], [R/W-0], + colspanx(8)[SB\<7:0\>], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) + #set align(left) + #grid( + columns: (auto, 1fr), + gutter: 1em, + [*bit 7-0*], [*SB\<7:0\>*: Serial data], + ) +] + +#reg-figure( + caption: [#hex("FF02") - SC - Serial control register] +)[ + #reg-table( + [R/W-0], [U], [U], [U], [U], [U], [U], [R/W-0], + [SIO_EN], unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), unimpl-bit(), [SIO_CLK], + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) + #set align(left) + #grid( + columns: (auto, 1fr), + gutter: 1em, + [*bit 7*], [*SIO_EN*], + [*bit 6-1*], [*Unimplemented*: Ignored during writes, reads are undefined], + [*bit 0*], [*SIO_CLK*] + ) +] diff --git a/common.typ b/common.typ new file mode 100644 index 0000000..ff6685a --- /dev/null +++ b/common.typ @@ -0,0 +1,55 @@ +#import "@preview/cetz:0.1.2" +#import "@preview/tablex:0.0.7": tablex, cellx, colspanx, hlinex + +#let monotext(..args) = text(font: "Anonymous Pro", fallback: false, ..args) + +#let awesome-brands(..args) = text(font: "Font Awesome 6 Brands", fallback: false, ..args) + +#let awesome(..args) = text(font: "Font Awesome 6 Free Solid", fallback: false, ..args) + +#let color-box(icon: none, title: none, fgcolor: black, bgcolor: white, title-color: white, content) = [ + #let radius = 5pt + #box(fill: bgcolor, stroke: 2pt + fgcolor, radius: radius)[ + #block(breakable: false, width: 100%, fill: fgcolor, inset: (x: 16pt, y: 8pt), below: 0pt, radius: (top-left: radius, top-right: radius))[ + #awesome(fill: title-color, icon) + #text(fill: title-color, weight: "bold")[#title] + ] + #block(breakable: false, width: 100%, inset: 16pt)[#content] + ] +] + +#let caveat(content) = [ + #color-box(content, icon: "\u{f05a}", title: "Caveat", fgcolor: rgb("#DC143C"), bgcolor: rgb("#FFE4E1")) +] +#let speculation(content) = [ + #color-box(content, icon: "\u{f12e}", title: "Speculation", fgcolor: rgb("#467BA9"), bgcolor: rgb("#F0FFFF")) +] +#let warning(content) = [ + #color-box(content, icon: "\u{f06a}", title: "Warning", fgcolor: rgb("#FFD700"), bgcolor: rgb("#FFFACD"), title-color: rgb("#505050")) +] + +#let bit(content) = monotext(content) +#let bin(content) = monotext("0b" + content) +#let hex(content) = monotext("0x" + content) +#let hex-range(start, end) = monotext({"0x" + start + "-0x" + end}) + +#let unimpl-bit() = cellx(fill: rgb("#D3D3D3ff"))[] + +#let reg-table(..args) = monotext(9pt)[ + #tablex( + columns: (1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr), + inset: 5pt, + align: center + horizon, + ..args + ) +] +#let reg-figure(caption: none, content) = [ + #v(1cm) + #figure( + content, + caption: caption, + kind: "register", + supplement: [Register], + numbering: (..nums) => counter(heading).display((..hnums) => numbering("1", hnums.pos().at(1))) + "." + numbering("1", ..nums) + ) +] diff --git a/config.tex b/config.tex deleted file mode 100644 index e30ed5e..0000000 --- a/config.tex +++ /dev/null @@ -1,7 +0,0 @@ -%!TEX root = gbctr.tex -%!TEX program = lualatex - -\renewcommand{\Revision}{@REVISION@} -\if @DRAFT@0 - \draft 0\relax -\fi diff --git a/gbctr.bib b/gbctr.bib deleted file mode 100644 index 8458fa5..0000000 --- a/gbctr.bib +++ /dev/null @@ -1,39 +0,0 @@ -@misc{pastraiser, - key={Pastraiser}, - title={Gameboy {CPU} ({LR35902}) instruction set}, - howpublished={\\\url{http://www.pastraiser.com/cpu/gameboy/gameboy_opcodes.html}} -} -@misc{tauwasser_mbc1, - title={{MBC1 - Tauwasser's Wiki}}, - author={Tauwasser}, - howpublished={\\\url{https://wiki.tauwasser.eu/view/MBC1}} -} -@misc{tauwasser_mbc2, - title={{MBC2 - Tauwasser's Wiki}}, - author={Tauwasser}, - howpublished={\\\url{https://wiki.tauwasser.eu/view/MBC2}} -} -@misc{pandocs, - title={{Pan Docs - Everything You Always Wanted To Know About GAMEBOY}}, - author={{Pan of ATX} and Fayzullin, Marat and Felber Pascal and Robson Paul and Korth Martin}, - howpublished={\\\url{http://bgb.bircd.org/pandocs.htm}} -} -@misc{tcagbd, - title={{The Cycle-Accurate Game Boy Docs}}, - author={Antonio Niño Díaz (AntonioND)}, - howpublished={\\\url{https://github.com/AntonioND/giibiiadvance/tree/master/docs}} -} -@misc{costis_sgb, - title={{The quest for dumping GameBoy Boot ROMs!}}, - author={Costis Sideris}, - howpublished={\\\url{http://www.its.caltech.edu/~costis/sgb_hack/}} -} -@misc{gekkio_sgb2, - title={{Dumping the Super Game Boy 2 boot ROM}}, - author={gekkio}, - howpublished={\\\url{https://gekkio.fi/blog/2015/dumping-the-super-game-boy-2-boot-rom/}} -} -@misc{sm831x, - title={{SM8311/SM8313/SM8314/SM8315 - 8-Bit Single-Chip Microcomputers (Controllers For Home Appliances)}}, - author={Sharp} -} diff --git a/gbctr.tex b/gbctr.tex deleted file mode 100644 index 312633c..0000000 --- a/gbctr.tex +++ /dev/null @@ -1,288 +0,0 @@ -%!TEX program = lualatex -\providecommand{\main}{.} - -\newcount\draft\draft=1 -\newcommand{\Revision}{} - -\include{config} - -\ifnum \draft=1 - \documentclass[a4paper, draft, oneside]{memoir} -\else - \documentclass[a4paper, oneside]{memoir} -\fi - -\usepackage[final]{graphicx} -\usepackage[english]{babel} -\usepackage{fontspec} -\usepackage[T1]{fontenc} -\usepackage{subcaption} -\usepackage{charter} -\usepackage{mathpazo} -\usepackage[ttdefault=true]{AnonymousPro} -\usepackage{tabularx} -\usepackage{pdflscape} -\usepackage{enumitem} -\usepackage{ccicons} -\usepackage[svgnames,table]{xcolor} -\usepackage{tikz} -\usepackage{tikz-timing} -\usepackage{wrapfig} -\usepackage{float} -\usepackage[cache=false]{minted} -\usepackage{enumitem} -\usepackage[many]{tcolorbox} -\usepackage{fontawesome} -\usepackage{hyperref} -\usepackage[all]{hypcap} -\usepackage{hyphenat} -\usepackage{subfiles} - -\usetikztiminglibrary{either} - -% aliases to support old fontawesome package versions -\providecommand\faArrowCircleDown{\faCircleArrowDown} -\providecommand\faArrowCircleUp{\faCircleArrowUp} -\providecommand\faArrowCircleRight{\faCircleArrowRight} -\providecommand\faArrowCircleLeft{\faCircleArrowLeft} -\providecommand\faExclamationCircle{\faExclamationSign} -\providecommand\faInfoCircle{\faInfoSign} - -\usemintedstyle{autumn} - -\graphicspath{{\main/images/}} - -\bibliographystyle{plain} - -\title{Game Boy: Complete Technical Reference} -\author{gekkio\\ \url{https://gekkio.fi}} -\renewcommand{\maketitlehookd}{ - \begin{center} - Revision \Revision - - \ifdraftdoc - DRAFT! - \fi - - \vfill - - \href{http://creativecommons.org/licenses/by-sa/4.0/}{\Huge \ccbysa} - - This work is licensed under a \href{http://creativecommons.org/licenses/by-sa/4.0/}{Creative Commons Attribution-ShareAlike 4.0 International License}. - \end{center} -} - -\ifdraftdoc - \makeevenfoot{plain}{}{\thepage}{\textit{DRAFT! \Revision}} - \makeoddfoot{plain}{\textit{DRAFT! \Revision}}{\thepage}{} - \makeevenfoot{headings}{}{}{\textit{DRAFT! \Revision}} - \makeoddfoot{headings}{\textit{DRAFT! \Revision}}{}{} -\fi - -\hypersetup{final,unicode=true,pdfborder={0 0 0},bookmarksnumbered=true,pdfpagemode=UseOutlines,pdfauthor=gekkio,pdftitle=\thetitle} - -\setlrmarginsandblock{2cm}{2cm}{*} -\setulmarginsandblock{2cm}{2cm}{*} -\checkandfixthelayout - -\newtcolorbox{speculation} -{colframe=SteelBlue,colback=Azure,title=\faPuzzlePiece,fonttitle=\small} - -\newtcolorbox{caveat} -{colframe=Crimson,colback=MistyRose,title=\faInfoCircle,fonttitle=\small} - -\newtcolorbox{warning} -{colframe=Gold,colback=LemonChiffon,title=\faExclamationCircle,fonttitle=\small} - -\floatstyle{plaintop} -\newfloat{register}{h}{lor}[chapter] -\floatname{register}{Register} - -\newcommand{\bit}[1]{\texttt{#1}} -\newcommand{\bin}[1]{\texttt{0b#1}} -\newcommand{\hex}[1]{\texttt{0x#1}} -\newcommand{\hexrange}[2]{\texttt{0x#1\hyp{}0x#2}} - -\newcolumntype{L}{>{\raggedright\arraybackslash}X} -\newcolumntype{C}{>{\centering\arraybackslash}X} -\newcolumntype{R}{>{\raggedleft\arraybackslash}X} - -\setcounter{tocdepth}{4} - -\begin{document} - -\hypersetup{pageanchor=false} - -\begin{titlingpage} - \calccentering{\unitlength} - \setlength{\droptitle}{80pt} - \begin{adjustwidth*}{\unitlength}{-\unitlength} - \maketitle - \end{adjustwidth*} -\end{titlingpage} - -\hypersetup{pageanchor=true} - -\chapter*{Preface} -\addcontentsline{toc}{chapter}{Preface} -\phantomsection - -\begin{caveat} - \Huge - IMPORTANT: This document focuses at the moment on 1st and 2nd generation - devices (models before the Game Boy Color), and some hardware details are - very different in later generations. - - \bigskip - - Be very careful if you make assumptions about later generation devices based - on this document! -\end{caveat} - -\chapter*{How to read this document} -\addcontentsline{toc}{chapter}{How to read this document} -\phantomsection - -\begin{speculation} - This is something that hasn't been verified, but would make a lot of sense. -\end{speculation} - -\begin{caveat} - This explains some caveat about this documentation that you should know. -\end{caveat} - -\begin{warning} - This is a warning about something. -\end{warning} - -\section{Formatting of numbers} - -When a single bit is discussed in isolation, the value looks like this: \bit{0}, \bit{1}. - -Binary numbers are prefixed with \bin{} like this: \bin{0101101}, \bin{11011}, \bin{00000000}. Values are prefixed with zeroes when necessary, so the total number of digits always matches the number of digits in the value. - -Hexadecimal numbers are prefixed with \hex{} like this: \hex{1234}, \hex{DEADBEEF}, \hex{FF04}. Values are prefixed with zeroes when necessary, so the total number of characters always matches the number of nibbles in the value. - -Examples: - -\vspace{0.5cm} - -\begin{tabular}{l l l l} - & 4-bit & 8-bit & 16-bit \\ - \hline - Binary & \bin{0101} & \bin{10100101} & \bin{0000101010100101} \\ - Hexadecimal & \hex{5} & \hex{A5} & \hex{0AA5} -\end{tabular} - -\section{Register definitions} - -\begin{register}[H] - \caption{\hex{1234} - This is a hardware register definition} - { - \ttfamily - \begin{tabularx}{\linewidth}{|C|C|C|C|C|C|C|C|} - \hline - R/W-0 & R/W-1 & U-1 & R-0 & R-1 & R-x & W-1 & U-0 \\ - \hline - \multicolumn{2}{|c|}{VALUE<1:0>} & \cellcolor{LightGray} - & \multicolumn{3}{c|}{BIGVAL<7:5>} & FLAG & \cellcolor{LightGray} - \\ - \hline - bit 7 & 6 & 5 & 4 & 3 & 2 & 1 & bit 0 \\ - \hline - \end{tabularx}{\parfillskip=0pt\par} - } - - \medskip - \textbf{Top row legend:} - \begin{description}[leftmargin=5em, style=nextline] - \item[R] - Bit can be read. - \item[W] - Bit can be written. If the bit cannot be read, reading returns a constant - value defined in the bit list of the register in question. - \item[U] - Unimplemented bit. Writing has no effect, and reading returns a constant - value defined in the bit list of the register in question. - \item[-n] - Value after system reset: \bit{0}, \bit{1}, or x. - \item[\bit{1}] - Bit is set. - \item[\bit{0}] - Bit is cleared. - \item[x] - Bit is unknown (e.g. depends on external things such as user input). - \end{description} - - \medskip - \textbf{Middle row legend:} - - { - \ttfamily - \begin{tabularx}{0.5\linewidth}{|L|C|} - \hline - VALUE<1:0> & \rmfamily Bits 1 and 0 of VALUE \\ - \hline - \cellcolor{LightGray} - & \rmfamily Unimplemented bit \\ - \hline - BIGVAL<7:5> & \rmfamily Bits 7, 6, 5 of BIGVAL \\ - \hline - FLAG & \rmfamily Single-bit value FLAG \\ - \hline - \end{tabularx} - } - - \vspace{3mm} - \textbf{In this example:} - \begin{itemize} - \item{After system reset, VALUE is \bin{01}, BIGVAL is either \bin{010} or \bin{011}, FLAG is \bin{1}.} - \item{Bits 5 and 0 are unimplemented. Bit 5 always returns \bit{1}, and bit 0 always returns \bit{0}.} - \item{Both bits of VALUE can be read and written. When this register is written, bit 7 of the written value goes to bit 1 of VALUE.} - \item{FLAG can only be written to, so reads return a value that is defined elsewhere.} - \item{BIGVAL cannot be written to. Only bits 5-7 of BIGVAL are defined here, so look elsewhere for the low bits 0-4.} - \end{itemize} -\end{register} - -\clearpage - -\tableofcontents - -\part{Sharp SM83 CPU core} - -\subfile{chapter/cpu/timing} -\subfile{chapter/cpu/instruction-set} - -\part{Game Boy SoC peripherals and features} - -\subfile{chapter/peripherals/boot-rom} -\subfile{chapter/peripherals/dma} -\subfile{chapter/peripherals/ppu} -\subfile{chapter/peripherals/p1} -\subfile{chapter/peripherals/serial} - -\part{Game Boy game cartridges} - -\subfile{chapter/cartridges/mbc1} -\subfile{chapter/cartridges/mbc2} -\subfile{chapter/cartridges/mbc3} -\subfile{chapter/cartridges/mbc30} -\subfile{chapter/cartridges/mbc5} -\subfile{chapter/cartridges/mbc6} -\subfile{chapter/cartridges/mbc7} -\subfile{chapter/cartridges/huc1} -\subfile{chapter/cartridges/huc3} -\subfile{chapter/cartridges/mmm01} -\subfile{chapter/cartridges/tama5} - -\part*{Appendices} -\addcontentsline{toc}{part}{Appendices} -\phantomsection - -\begin{appendices} -\subfile{appendix/opcode-tables} -\subfile{appendix/memory-map} -\subfile{appendix/external-bus} -\subfile{appendix/pinouts} -\end{appendices} - -\bibliography{gbctr} - -\end{document} diff --git a/gbctr.typ b/gbctr.typ new file mode 100644 index 0000000..2c515c3 --- /dev/null +++ b/gbctr.typ @@ -0,0 +1,188 @@ +#import "common.typ": awesome-brands, monotext + +#let title = [Game Boy: Complete Technical Reference] +#let date = datetime.today() +#let config = json("config.json") + +#set document(title: title, author: ("gekkio"), date: date) +#set par(justify: true) +#set text(font: "Noto Sans") +#show figure.where(kind: "register"): set figure.caption(position: top) +#show raw: set text(1.25em, font: "Anonymous Pro", fallback: false) +#set figure(numbering: (..nums) => [#counter(heading).display((..heading_nums) => heading_nums.pos().at(1))\.#nums.pos().at(0)]) + +#[ + #set align(center) + #set par(justify: false) + #set page(margin: (x: 2cm, y: 5cm), footer-descent: 0%, footer: [ + #link("http://creativecommons.org/licenses/by-sa/4.0/")[ + #text(17pt)[ + #awesome-brands[\u{f25e}] + #awesome-brands[\u{f4e7}] + #awesome-brands[\u{f4ef}] + ] + ]\ + This work is licensed under a #link("http://creativecommons.org/licenses/by-sa/4.0/")[Creative Commons Attribution-ShareAlike 4.0 International License]. + ]) + + #text(17pt)[#title] + + gekkio\ + #monotext[#link("https://gekkio.fi")] + + #date.display("[month repr:long] [day padding:none], [year]") + + Revision #config.revision + #if config.draft [\ DRAFT!] +] + +#set page( + margin: (x: 2cm, y: 2cm), + footer: [ + #set text(10pt) + #block(width: 100%)[ + #set align(center) + #place(left, text(style: "italic")[ + #if config.draft [DRAFT!] + #config.revision + ]) + #box(counter(page).display()) + ] + ] +) +#show heading: set block(above: 1.4em, below: 1em) + +#set heading(numbering: (..nums) => { + let level = nums.pos().len() + if level <= 2 { + none + } else { + numbering("1.1.1.1.1", ..nums.pos().slice(1)) + } +}) +#include("preface.typ") + +#pagebreak() +#show outline: set heading(outlined: true) +#show outline.entry: it => { + if it.level == 1 { + block(above: 20pt, below: 0pt, strong(it)) + } else if it.level == 2 { + strong(it) + } else { + it + } +} +#outline(fill: repeat(" . "), indent: n => calc.max(0, n - 1) * 1em) + +#set heading(numbering: (..nums) => { + let level = nums.pos().len() + if level == 1 { + numbering("I", ..nums) + } else if level > 3 { + none + } else { + numbering("1.1.1.1.1", ..nums.pos().slice(1)) + } +}) + +#counter(heading).update(0) +#[ + #show heading.where(level: 1): it => [ + #pagebreak() + #set align(center) + #text(21pt)[ + #v(1fr) + #block("Part " + counter(heading).display()) + #block(it.body) + #v(1fr) + ] + #locate(loc => { + // Don't reset heading numbering when a new part starts + let cnt = counter(heading) + let elems = query(selector(heading).before(loc), loc) + elems.pop() + let part = cnt.at(loc).at(0) + let rest = cnt.at(elems.last().location()).slice(1) + return cnt.update((part, ..rest)) + }) + ] + #show heading.where(level: 2): it => [ + #pagebreak() + #block[ + #text(17pt, [Chapter #counter(heading).display()]) + ] + #text(21pt, it.body) + #v(1em) + #counter(figure).update(0) + #counter(figure.where(kind: table)).update(0) + #counter(figure.where(kind: "register")).update(0) + ] + + = Sharp SM83 CPU core + #include "chapter/cpu/timing.typ" + #include "chapter/cpu/instruction-set.typ" + + = Game Boy SoC peripherals and features + + #include "chapter/peripherals/boot-rom.typ" + #include "chapter/peripherals/dma.typ" + #include "chapter/peripherals/ppu.typ" + #include "chapter/peripherals/p1.typ" + #include "chapter/peripherals/serial.typ" + + = Game Boy game cartridges + + #include "chapter/cartridges/mbc1.typ" + #include "chapter/cartridges/mbc2.typ" + #include "chapter/cartridges/mbc3.typ" + #include "chapter/cartridges/mbc30.typ" + #include "chapter/cartridges/mbc5.typ" + #include "chapter/cartridges/mbc6.typ" + #include "chapter/cartridges/mbc7.typ" + #include "chapter/cartridges/huc1.typ" + #include "chapter/cartridges/huc3.typ" + #include "chapter/cartridges/mmm01.typ" + #include "chapter/cartridges/tama5.typ" +] + +#counter(heading).update(0) +#set heading(numbering: (..nums) => { + let level = nums.pos().len() + if level == 1 { + none + } else { + numbering("A.1.1.1.1", ..nums.pos().slice(1)) + } +}) +#set figure(numbering: (..nums) => [#counter(heading).display((..heading_nums) => numbering("A", heading_nums.pos().at(1)))\.#nums.pos().at(0)]) + +#pagebreak() +#text(21pt)[ + #set align(center) + #v(1fr) + = Appendices + #v(1fr) +] + +#show heading.where( + level: 2 +): it => block[ + #block[ + #text(17pt, [Appendix #counter(heading).display()]) + ] + #text(21pt, it.body) + #v(1em) +] + +#pagebreak() +#include "appendix/opcode-tables.typ" +#pagebreak() +#include "appendix/memory-map.typ" +#pagebreak() +#include "appendix/external-bus.typ" +#pagebreak() +#include "appendix/pinouts.typ" + +#pagebreak() +#bibliography("gbctr.yml") diff --git a/gbctr.yml b/gbctr.yml new file mode 100644 index 0000000..e46f42b --- /dev/null +++ b/gbctr.yml @@ -0,0 +1,38 @@ +pastraiser: + type: web + title: Gameboy CPU (LR35902) instruction set + url: http://www.pastraiser.com/cpu/gameboy/gameboy_opcodes.html +tauwasser_mbc1: + type: web + title: MBC1 - Tauwasser's Wiki + author: Tauwasser + url: https://wiki.tauwasser.eu/view/MBC1 +tauwasser_mbc2: + type: web + title: MBC2 - Tauwasser's Wiki + author: Tauwasser + url: https://wiki.tauwasser.eu/view/MBC2 +pandocs: + type: web + title: Pan Docs - Everything You Always Wanted To Know About GAMEBOY + author: ["Pan of ATX", "Fayzullin, Marat", "Felber, Pascal", "Robson, Paul", "Korth, Martin"] + url: http://bgb.bircd.org/pandocs.htm +tcagbd: + type: web + title: The Cycle-Accurate Game Boy Docs + author: Antonio Niño Díaz (AntonioND) + url: https://github.com/AntonioND/giibiiadvance/tree/master/docs +costis_sgb: + type: blog + title: The quest for dumping GameBoy Boot ROMs! + author: Costis Sideris + url: http://www.its.caltech.edu/~costis/sgb_hack/ +gekkio_sgb2: + type: blog + title: Dumping the Super Game Boy 2 boot ROM + author: gekkio + url: https://gekkio.fi/blog/2015/dumping-the-super-game-boy-2-boot-rom/ +sm831x: + type: reference + title: SM8311/SM8313/SM8314/SM8315 - 8-Bit Single-Chip Microcomputers (Controllers For Home Appliances) + publisher: Sharp Corporation diff --git a/images/DMG-CPU-pinout.pdf b/images/DMG-CPU-pinout.pdf deleted file mode 100644 index 35ecb9eb82c7c4f3b3e7e514d5fd1a94af377c54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21345 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 diff --git a/images/DMG-CPU-pinout.svg b/images/DMG-CPU-pinout.svg new file mode 100644 index 0000000..991a9da --- /dev/null +++ b/images/DMG-CPU-pinout.svg @@ -0,0 +1,881 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/MBC1-pinout.pdf b/images/MBC1-pinout.pdf deleted file mode 100644 index 0223ba7b9e0db329779ff897c0567583f6446426..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7668 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 S`NL2YDkuWr;85070sJ5OV?PZ5 diff --git a/images/MBC1-pinout.svg b/images/MBC1-pinout.svg new file mode 100644 index 0000000..4f2ff86 --- /dev/null +++ b/images/MBC1-pinout.svg @@ -0,0 +1,312 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/MBC2-pinout.pdf b/images/MBC2-pinout.pdf deleted file mode 100644 index 952ebb0d91c50c493f1abc795618c70fd6f17c73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8283 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/MBC5-pinout.pdf b/images/MBC5-pinout.pdf deleted file mode 100644 index 879e120956c0323cc823065c2d06af499b110f89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9904 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 Zl}u+BBPW-?JmlfzWCc)By;qa~{0|mKk8}V4 diff --git a/images/MBC5-pinout.svg b/images/MBC5-pinout.svg new file mode 100644 index 0000000..7c4b7ce --- /dev/null +++ b/images/MBC5-pinout.svg @@ -0,0 +1,391 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/MGB-CPU-pinout.pdf b/images/MGB-CPU-pinout.pdf deleted file mode 100644 index 0c6c6e340f79a19295afcd12073e936098114cd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21256 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 diff --git a/images/MGB-CPU-pinout.svg b/images/MGB-CPU-pinout.svg new file mode 100644 index 0000000..7f0bc46 --- /dev/null +++ b/images/MGB-CPU-pinout.svg @@ -0,0 +1,879 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/justfile b/justfile new file mode 100644 index 0000000..25e4aa7 --- /dev/null +++ b/justfile @@ -0,0 +1,16 @@ +draft := if env("GITHUB_REF", "") == "refs/heads/main" { "false" } else { "true" } +revision := if draft == "true" { + `git symbolic-ref --short HEAD` + "-" + `git rev-list --count HEAD` + "[" + `git rev-parse --short HEAD` + "]" +} else { + `git rev-list --count main` +} +config := "{\"draft\": true, \"revision\": \"main-127[0dc4a5]\"}" + +build: write-config + typst compile {{justfile_directory()}}/gbctr.typ + +watch: write-config + typst watch {{justfile_directory()}}/gbctr.typ + +write-config: + echo '{"draft": {{draft}}, "revision": "{{revision}}"}' > {{justfile_directory()}}/config.json diff --git a/opcodes.toml b/opcodes.toml new file mode 100644 index 0000000..dca8032 --- /dev/null +++ b/opcodes.toml @@ -0,0 +1,624 @@ +opcodes = [ + { mnemonic = "NOP", category = "NOP" }, + { mnemonic = "LD BC,nn", category = "LD_rr_nn" }, + { mnemonic = "LD (BC),A", category = "LD_bc_a" }, + { mnemonic = "INC BC", category = "INC_rr" }, + { mnemonic = "INC B", category = "INC_r" }, + { mnemonic = "DEC B", category = "DEC_r" }, + { mnemonic = "LD B,n", category = "LD_r_n" }, + { mnemonic = "RLCA", category = "RLCA" }, + { mnemonic = "LD (nn),SP", category = "LD_nn_sp" }, + { mnemonic = "ADD HL,BC", category = "ADD_hl_rr" }, + { mnemonic = "LD A,(BC)", category = "LD_a_bc" }, + { mnemonic = "DEC BC", category = "DEC_rr" }, + { mnemonic = "INC C", category = "INC_r" }, + { mnemonic = "DEC C", category = "DEC_r" }, + { mnemonic = "LD C,n", category = "LD_r_n" }, + { mnemonic = "RRCA", category = "RRCA" }, + { mnemonic = "STOP", category = "STOP" }, + { mnemonic = "LD DE,nn", category = "LD_rr_nn" }, + { mnemonic = "LD (DE),A", category = "LD_de_a" }, + { mnemonic = "INC DE", category = "INC_rr" }, + { mnemonic = "INC D", category = "INC_r" }, + { mnemonic = "DEC D", category = "DEC_r" }, + { mnemonic = "LD D,n", category = "LD_r_n" }, + { mnemonic = "RLA", category = "RLA" }, + { mnemonic = "JR e", category = "JR" }, + { mnemonic = "ADD HL,DE", category = "ADD_hl_rr" }, + { mnemonic = "LD A,(DE)", category = "LD_a_de" }, + { mnemonic = "DEC DE", category = "DEC_rr" }, + { mnemonic = "INC E", category = "INC_r" }, + { mnemonic = "DEC E", category = "DEC_r" }, + { mnemonic = "LD E,n", category = "LD_r_n" }, + { mnemonic = "RRA", category = "RRA" }, + { mnemonic = "JR NZ,e", category = "JR_cc" }, + { mnemonic = "LD HL,nn", category = "LD_rr_nn" }, + { mnemonic = "LD (HL+),A", category = "LD_hli_a" }, + { mnemonic = "INC HL", category = "INC_rr" }, + { mnemonic = "INC H", category = "INC_r" }, + { mnemonic = "DEC H", category = "DEC_r" }, + { mnemonic = "LD H,n", category = "LD_r_n" }, + { mnemonic = "DAA", category = "DAA" }, + { mnemonic = "JR Z,e", category = "JR_cc" }, + { mnemonic = "ADD HL,HL", category = "ADD_hl_rr" }, + { mnemonic = "LD A,(HL+)", category = "LD_a_hli" }, + { mnemonic = "DEC HL", category = "DEC_rr" }, + { mnemonic = "INC L", category = "INC_r" }, + { mnemonic = "DEC L", category = "DEC_r" }, + { mnemonic = "LD L,n", category = "LD_r_n" }, + { mnemonic = "CPL", category = "CPL" }, + { mnemonic = "JR NC,e", category = "JR_cc" }, + { mnemonic = "LD SP,nn", category = "LD_rr_nn" }, + { mnemonic = "LD (HL-),A", category = "LD_hld_a" }, + { mnemonic = "INC SP", category = "INC_rr" }, + { mnemonic = "INC (HL)", category = "INC_hl" }, + { mnemonic = "DEC (HL)", category = "DEC_hl" }, + { mnemonic = "LD (HL),n", category = "LD_hl_n" }, + { mnemonic = "SCF", category = "SCF" }, + { mnemonic = "JR C,e", category = "JR_cc" }, + { mnemonic = "ADD HL,SP", category = "ADD_hl_rr" }, + { mnemonic = "LD A,(HL-)", category = "LD_a_hld" }, + { mnemonic = "DEC SP", category = "DEC_rr" }, + { mnemonic = "INC A", category = "INC_r" }, + { mnemonic = "DEC A", category = "DEC_r" }, + { mnemonic = "LD A,n", category = "LD_r_n" }, + { mnemonic = "CCF", category = "CCF" }, + { mnemonic = "LD B,B", category = "LD_r_r" }, + { mnemonic = "LD B,C", category = "LD_r_r" }, + { mnemonic = "LD B,D", category = "LD_r_r" }, + { mnemonic = "LD B,E", category = "LD_r_r" }, + { mnemonic = "LD B,H", category = "LD_r_r" }, + { mnemonic = "LD B,L", category = "LD_r_r" }, + { mnemonic = "LD B,(HL)", category = "LD_r_hl" }, + { mnemonic = "LD B,A", category = "LD_r_r" }, + { mnemonic = "LD C,B", category = "LD_r_r" }, + { mnemonic = "LD C,C", category = "LD_r_r" }, + { mnemonic = "LD C,D", category = "LD_r_r" }, + { mnemonic = "LD C,E", category = "LD_r_r" }, + { mnemonic = "LD C,H", category = "LD_r_r" }, + { mnemonic = "LD C,L", category = "LD_r_r" }, + { mnemonic = "LD C,(HL)", category = "LD_r_hl" }, + { mnemonic = "LD C,A", category = "LD_r_r" }, + { mnemonic = "LD D,B", category = "LD_r_r" }, + { mnemonic = "LD D,C", category = "LD_r_r" }, + { mnemonic = "LD D,D", category = "LD_r_r" }, + { mnemonic = "LD D,E", category = "LD_r_r" }, + { mnemonic = "LD D,H", category = "LD_r_r" }, + { mnemonic = "LD D,L", category = "LD_r_r" }, + { mnemonic = "LD D,(HL)", category = "LD_r_hl" }, + { mnemonic = "LD D,A", category = "LD_r_r" }, + { mnemonic = "LD E,B", category = "LD_r_r" }, + { mnemonic = "LD E,C", category = "LD_r_r" }, + { mnemonic = "LD E,D", category = "LD_r_r" }, + { mnemonic = "LD E,E", category = "LD_r_r" }, + { mnemonic = "LD E,H", category = "LD_r_r" }, + { mnemonic = "LD E,L", category = "LD_r_r" }, + { mnemonic = "LD E,(HL)", category = "LD_r_hl" }, + { mnemonic = "LD E,A", category = "LD_r_r" }, + { mnemonic = "LD H,B", category = "LD_r_r" }, + { mnemonic = "LD H,C", category = "LD_r_r" }, + { mnemonic = "LD H,D", category = "LD_r_r" }, + { mnemonic = "LD H,E", category = "LD_r_r" }, + { mnemonic = "LD H,H", category = "LD_r_r" }, + { mnemonic = "LD H,L", category = "LD_r_r" }, + { mnemonic = "LD H,(HL)", category = "LD_r_hl" }, + { mnemonic = "LD H,A", category = "LD_r_r" }, + { mnemonic = "LD L,B", category = "LD_r_r" }, + { mnemonic = "LD L,C", category = "LD_r_r" }, + { mnemonic = "LD L,D", category = "LD_r_r" }, + { mnemonic = "LD L,E", category = "LD_r_r" }, + { mnemonic = "LD L,H", category = "LD_r_r" }, + { mnemonic = "LD L,L", category = "LD_r_r" }, + { mnemonic = "LD L,(HL)", category = "LD_r_hl" }, + { mnemonic = "LD L,A", category = "LD_r_r" }, + { mnemonic = "LD (HL),B", category = "LD_hl_r" }, + { mnemonic = "LD (HL),C", category = "LD_hl_r" }, + { mnemonic = "LD (HL),D", category = "LD_hl_r" }, + { mnemonic = "LD (HL),E", category = "LD_hl_r" }, + { mnemonic = "LD (HL),H", category = "LD_hl_r" }, + { mnemonic = "LD (HL),L", category = "LD_hl_r" }, + { mnemonic = "HALT", category = "HALT" }, + { mnemonic = "LD (HL),A", category = "LD_hl_r" }, + { mnemonic = "LD A,B", category = "LD_r_r" }, + { mnemonic = "LD A,C", category = "LD_r_r" }, + { mnemonic = "LD A,D", category = "LD_r_r" }, + { mnemonic = "LD A,E", category = "LD_r_r" }, + { mnemonic = "LD A,H", category = "LD_r_r" }, + { mnemonic = "LD A,L", category = "LD_r_r" }, + { mnemonic = "LD A,(HL)", category = "LD_r_hl" }, + { mnemonic = "LD A,A", category = "LD_r_r" }, + { mnemonic = "ADD B", category = "ADD_r" }, + { mnemonic = "ADD C", category = "ADD_r" }, + { mnemonic = "ADD D", category = "ADD_r" }, + { mnemonic = "ADD E", category = "ADD_r" }, + { mnemonic = "ADD H", category = "ADD_r" }, + { mnemonic = "ADD L", category = "ADD_r" }, + { mnemonic = "ADD (HL)", category = "ADD_hl" }, + { mnemonic = "ADD A", category = "ADD_r" }, + { mnemonic = "ADC B", category = "ADC_r" }, + { mnemonic = "ADC C", category = "ADC_r" }, + { mnemonic = "ADC D", category = "ADC_r" }, + { mnemonic = "ADC E", category = "ADC_r" }, + { mnemonic = "ADC H", category = "ADC_r" }, + { mnemonic = "ADC L", category = "ADC_r" }, + { mnemonic = "ADC (HL)", category = "ADC_hl" }, + { mnemonic = "ADC A", category = "ADC_r" }, + { mnemonic = "SUB B", category = "SUB_r" }, + { mnemonic = "SUB C", category = "SUB_r" }, + { mnemonic = "SUB D", category = "SUB_r" }, + { mnemonic = "SUB E", category = "SUB_r" }, + { mnemonic = "SUB H", category = "SUB_r" }, + { mnemonic = "SUB L", category = "SUB_r" }, + { mnemonic = "SUB (HL)", category = "SUB_hl" }, + { mnemonic = "SUB A", category = "SUB_r" }, + { mnemonic = "SBC B", category = "SBC_r" }, + { mnemonic = "SBC C", category = "SBC_r" }, + { mnemonic = "SBC D", category = "SBC_r" }, + { mnemonic = "SBC E", category = "SBC_r" }, + { mnemonic = "SBC H", category = "SBC_r" }, + { mnemonic = "SBC L", category = "SBC_r" }, + { mnemonic = "SBC (HL)", category = "SBC_hl" }, + { mnemonic = "SBC A", category = "SBC_r" }, + { mnemonic = "AND B", category = "AND_r" }, + { mnemonic = "AND C", category = "AND_r" }, + { mnemonic = "AND D", category = "AND_r" }, + { mnemonic = "AND E", category = "AND_r" }, + { mnemonic = "AND H", category = "AND_r" }, + { mnemonic = "AND L", category = "AND_r" }, + { mnemonic = "AND (HL)", category = "AND_hl" }, + { mnemonic = "AND A", category = "AND_r" }, + { mnemonic = "XOR B", category = "XOR_r" }, + { mnemonic = "XOR C", category = "XOR_r" }, + { mnemonic = "XOR D", category = "XOR_r" }, + { mnemonic = "XOR E", category = "XOR_r" }, + { mnemonic = "XOR H", category = "XOR_r" }, + { mnemonic = "XOR L", category = "XOR_r" }, + { mnemonic = "XOR (HL)", category = "XOR_hl" }, + { mnemonic = "XOR A", category = "XOR_r" }, + { mnemonic = "OR B", category = "OR_r" }, + { mnemonic = "OR C", category = "OR_r" }, + { mnemonic = "OR D", category = "OR_r" }, + { mnemonic = "OR E", category = "OR_r" }, + { mnemonic = "OR H", category = "OR_r" }, + { mnemonic = "OR L", category = "OR_r" }, + { mnemonic = "OR (HL)", category = "OR_hl" }, + { mnemonic = "OR A", category = "OR_r" }, + { mnemonic = "CP B", category = "CP_r" }, + { mnemonic = "CP C", category = "CP_r" }, + { mnemonic = "CP D", category = "CP_r" }, + { mnemonic = "CP E", category = "CP_r" }, + { mnemonic = "CP H", category = "CP_r" }, + { mnemonic = "CP L", category = "CP_r" }, + { mnemonic = "CP (HL)", category = "CP_hl" }, + { mnemonic = "CP A", category = "CP_r" }, + { mnemonic = "RET NZ", category = "RET_cc" }, + { mnemonic = "POP BC", category = "POP_rr" }, + { mnemonic = "JP NZ,nn", category = "JP_cc" }, + { mnemonic = "JP nn", category = "JP" }, + { mnemonic = "CALL NZ,nn", category = "CALL_cc" }, + { mnemonic = "PUSH BC", category = "PUSH_rr" }, + { mnemonic = "ADD n", category = "ADD_n" }, + { mnemonic = "RST 0x00", category = "RST" }, + { mnemonic = "RET Z", category = "RET_cc" }, + { mnemonic = "RET", category = "RET" }, + { mnemonic = "JP Z,nn", category = "JP_cc" }, + { mnemonic = "CB op", category = "CB" }, + { mnemonic = "CALL Z,nn", category = "CALL_cc" }, + { mnemonic = "CALL nn", category = "CALL" }, + { mnemonic = "ADC n", category = "ADC_n" }, + { mnemonic = "RST 0x08", category = "RST" }, + { mnemonic = "RET NC", category = "RET_cc" }, + { mnemonic = "POP DE", category = "POP_rr" }, + { mnemonic = "JP NC,nn", category = "JP_cc" }, + { mnemonic = "-", category = "undefined" }, + { mnemonic = "CALL NC,nn", category = "CALL_cc" }, + { mnemonic = "PUSH DE", category = "PUSH_rr" }, + { mnemonic = "SUB n", category = "SUB_n" }, + { mnemonic = "RST 0x10", category = "RST" }, + { mnemonic = "RET C", category = "RET_cc" }, + { mnemonic = "RETI", category = "RETI" }, + { mnemonic = "JP C,nn", category = "JP_cc" }, + { mnemonic = "-", category = "undefined" }, + { mnemonic = "CALL C,nn", category = "CALL_cc" }, + { mnemonic = "-", category = "undefined" }, + { mnemonic = "SBC n", category = "SBC_n" }, + { mnemonic = "RST 0x18", category = "RST" }, + { mnemonic = "LDH (n),A", category = "LDH_n_a" }, + { mnemonic = "POP HL", category = "POP_rr" }, + { mnemonic = "LDH (C),A", category = "LDH_c_a" }, + { mnemonic = "-", category = "undefined" }, + { mnemonic = "-", category = "undefined" }, + { mnemonic = "PUSH HL", category = "PUSH_rr" }, + { mnemonic = "AND n", category = "AND_n" }, + { mnemonic = "RST 0x20", category = "RST" }, + { mnemonic = "ADD SP,e", category = "ADD_sp_e" }, + { mnemonic = "JP HL", category = "JP_hl" }, + { mnemonic = "LD (nn),A", category = "LD_nn_a" }, + { mnemonic = "-", category = "undefined" }, + { mnemonic = "-", category = "undefined" }, + { mnemonic = "-", category = "undefined" }, + { mnemonic = "XOR n", category = "XOR_n" }, + { mnemonic = "RST 0x28", category = "RST" }, + { mnemonic = "LDH A,(n)", category = "LDH_a_n" }, + { mnemonic = "POP AF", category = "POP_rr" }, + { mnemonic = "LDH A,(C)", category = "LDH_a_c" }, + { mnemonic = "DI", category = "DI" }, + { mnemonic = "-", category = "undefined" }, + { mnemonic = "PUSH AF", category = "PUSH_rr" }, + { mnemonic = "OR n", category = "OR_n" }, + { mnemonic = "RST 0x30", category = "RST" }, + { mnemonic = "LD HL,SP+e", category = "LD_hl_sp_e" }, + { mnemonic = "LD SP,HL", category = "LD_sp_hl" }, + { mnemonic = "LD A,(nn)", category = "LD_a_nn" }, + { mnemonic = "EI", category = "EI" }, + { mnemonic = "-", category = "undefined" }, + { mnemonic = "-", category = "undefined" }, + { mnemonic = "CP n", category = "CP_n" }, + { mnemonic = "RST 0x38", category = "RST" }, +] + +cb_opcodes = [ + { mnemonic = "RLC B", category = "RLC_r" }, + { mnemonic = "RLC C", category = "RLC_r" }, + { mnemonic = "RLC D", category = "RLC_r" }, + { mnemonic = "RLC E", category = "RLC_r" }, + { mnemonic = "RLC H", category = "RLC_r" }, + { mnemonic = "RLC L", category = "RLC_r" }, + { mnemonic = "RLC (HL)", category = "RLC_hl" }, + { mnemonic = "RLC A", category = "RLC_r" }, + { mnemonic = "RRC B", category = "RRC_r" }, + { mnemonic = "RRC C", category = "RRC_r" }, + { mnemonic = "RRC D", category = "RRC_r" }, + { mnemonic = "RRC E", category = "RRC_r" }, + { mnemonic = "RRC H", category = "RRC_r" }, + { mnemonic = "RRC L", category = "RRC_r" }, + { mnemonic = "RRC (HL)", category = "RRC_hl" }, + { mnemonic = "RRC A", category = "RRC_r" }, + { mnemonic = "RL B", category = "RL_r" }, + { mnemonic = "RL C", category = "RL_r" }, + { mnemonic = "RL D", category = "RL_r" }, + { mnemonic = "RL E", category = "RL_r" }, + { mnemonic = "RL H", category = "RL_r" }, + { mnemonic = "RL L", category = "RL_r" }, + { mnemonic = "RL (HL)", category = "RL_hl" }, + { mnemonic = "RL A", category = "RL_r" }, + { mnemonic = "RR B", category = "RR_r" }, + { mnemonic = "RR C", category = "RR_r" }, + { mnemonic = "RR D", category = "RR_r" }, + { mnemonic = "RR E", category = "RR_r" }, + { mnemonic = "RR H", category = "RR_r" }, + { mnemonic = "RR L", category = "RR_r" }, + { mnemonic = "RR (HL)", category = "RR_hl" }, + { mnemonic = "RR A", category = "RR_r" }, + { mnemonic = "SLA B", category = "SLA_r" }, + { mnemonic = "SLA C", category = "SLA_r" }, + { mnemonic = "SLA D", category = "SLA_r" }, + { mnemonic = "SLA E", category = "SLA_r" }, + { mnemonic = "SLA H", category = "SLA_r" }, + { mnemonic = "SLA L", category = "SLA_r" }, + { mnemonic = "SLA (HL)", category = "SLA_hl" }, + { mnemonic = "SLA A", category = "SLA_r" }, + { mnemonic = "SRA B", category = "SRA_r" }, + { mnemonic = "SRA C", category = "SRA_r" }, + { mnemonic = "SRA D", category = "SRA_r" }, + { mnemonic = "SRA E", category = "SRA_r" }, + { mnemonic = "SRA H", category = "SRA_r" }, + { mnemonic = "SRA L", category = "SRA_r" }, + { mnemonic = "SRA (HL)", category = "SRA_hl" }, + { mnemonic = "SRA A", category = "SRA_r" }, + { mnemonic = "SWAP B", category = "SWAP_r" }, + { mnemonic = "SWAP C", category = "SWAP_r" }, + { mnemonic = "SWAP D", category = "SWAP_r" }, + { mnemonic = "SWAP E", category = "SWAP_r" }, + { mnemonic = "SWAP H", category = "SWAP_r" }, + { mnemonic = "SWAP L", category = "SWAP_r" }, + { mnemonic = "SWAP (HL)", category = "SWAP_hl" }, + { mnemonic = "SWAP A", category = "SWAP_r" }, + { mnemonic = "SRL B", category = "SRL_r" }, + { mnemonic = "SRL C", category = "SRL_r" }, + { mnemonic = "SRL D", category = "SRL_r" }, + { mnemonic = "SRL E", category = "SRL_r" }, + { mnemonic = "SRL H", category = "SRL_r" }, + { mnemonic = "SRL L", category = "SRL_r" }, + { mnemonic = "SRL (HL)", category = "SRL_hl" }, + { mnemonic = "SRL A", category = "SRL_r" }, + { mnemonic = "BIT 0,B", category = "BIT_b_r" }, + { mnemonic = "BIT 0,C", category = "BIT_b_r" }, + { mnemonic = "BIT 0,D", category = "BIT_b_r" }, + { mnemonic = "BIT 0,E", category = "BIT_b_r" }, + { mnemonic = "BIT 0,H", category = "BIT_b_r" }, + { mnemonic = "BIT 0,L", category = "BIT_b_r" }, + { mnemonic = "BIT 0,(HL)", category = "BIT_b_hl" }, + { mnemonic = "BIT 0,A", category = "BIT_b_r" }, + { mnemonic = "BIT 1,B", category = "BIT_b_r" }, + { mnemonic = "BIT 1,C", category = "BIT_b_r" }, + { mnemonic = "BIT 1,D", category = "BIT_b_r" }, + { mnemonic = "BIT 1,E", category = "BIT_b_r" }, + { mnemonic = "BIT 1,H", category = "BIT_b_r" }, + { mnemonic = "BIT 1,L", category = "BIT_b_r" }, + { mnemonic = "BIT 1,(HL)", category = "BIT_b_hl" }, + { mnemonic = "BIT 1,A", category = "BIT_b_r" }, + { mnemonic = "BIT 2,B", category = "BIT_b_r" }, + { mnemonic = "BIT 2,C", category = "BIT_b_r" }, + { mnemonic = "BIT 2,D", category = "BIT_b_r" }, + { mnemonic = "BIT 2,E", category = "BIT_b_r" }, + { mnemonic = "BIT 2,H", category = "BIT_b_r" }, + { mnemonic = "BIT 2,L", category = "BIT_b_r" }, + { mnemonic = "BIT 2,(HL)", category = "BIT_b_hl" }, + { mnemonic = "BIT 2,A", category = "BIT_b_r" }, + { mnemonic = "BIT 3,B", category = "BIT_b_r" }, + { mnemonic = "BIT 3,C", category = "BIT_b_r" }, + { mnemonic = "BIT 3,D", category = "BIT_b_r" }, + { mnemonic = "BIT 3,E", category = "BIT_b_r" }, + { mnemonic = "BIT 3,H", category = "BIT_b_r" }, + { mnemonic = "BIT 3,L", category = "BIT_b_r" }, + { mnemonic = "BIT 3,(HL)", category = "BIT_b_hl" }, + { mnemonic = "BIT 3,A", category = "BIT_b_r" }, + { mnemonic = "BIT 4,B", category = "BIT_b_r" }, + { mnemonic = "BIT 4,C", category = "BIT_b_r" }, + { mnemonic = "BIT 4,D", category = "BIT_b_r" }, + { mnemonic = "BIT 4,E", category = "BIT_b_r" }, + { mnemonic = "BIT 4,H", category = "BIT_b_r" }, + { mnemonic = "BIT 4,L", category = "BIT_b_r" }, + { mnemonic = "BIT 4,(HL)", category = "BIT_b_hl" }, + { mnemonic = "BIT 4,A", category = "BIT_b_r" }, + { mnemonic = "BIT 5,B", category = "BIT_b_r" }, + { mnemonic = "BIT 5,C", category = "BIT_b_r" }, + { mnemonic = "BIT 5,D", category = "BIT_b_r" }, + { mnemonic = "BIT 5,E", category = "BIT_b_r" }, + { mnemonic = "BIT 5,H", category = "BIT_b_r" }, + { mnemonic = "BIT 5,L", category = "BIT_b_r" }, + { mnemonic = "BIT 5,(HL)", category = "BIT_b_hl" }, + { mnemonic = "BIT 5,A", category = "BIT_b_r" }, + { mnemonic = "BIT 6,B", category = "BIT_b_r" }, + { mnemonic = "BIT 6,C", category = "BIT_b_r" }, + { mnemonic = "BIT 6,D", category = "BIT_b_r" }, + { mnemonic = "BIT 6,E", category = "BIT_b_r" }, + { mnemonic = "BIT 6,H", category = "BIT_b_r" }, + { mnemonic = "BIT 6,L", category = "BIT_b_r" }, + { mnemonic = "BIT 6,(HL)", category = "BIT_b_hl" }, + { mnemonic = "BIT 6,A", category = "BIT_b_r" }, + { mnemonic = "BIT 7,B", category = "BIT_b_r" }, + { mnemonic = "BIT 7,C", category = "BIT_b_r" }, + { mnemonic = "BIT 7,D", category = "BIT_b_r" }, + { mnemonic = "BIT 7,E", category = "BIT_b_r" }, + { mnemonic = "BIT 7,H", category = "BIT_b_r" }, + { mnemonic = "BIT 7,L", category = "BIT_b_r" }, + { mnemonic = "BIT 7,(HL)", category = "BIT_b_hl" }, + { mnemonic = "BIT 7,A", category = "BIT_b_r" }, + { mnemonic = "RES 0,B", category = "RES_b_r" }, + { mnemonic = "RES 0,C", category = "RES_b_r" }, + { mnemonic = "RES 0,D", category = "RES_b_r" }, + { mnemonic = "RES 0,E", category = "RES_b_r" }, + { mnemonic = "RES 0,H", category = "RES_b_r" }, + { mnemonic = "RES 0,L", category = "RES_b_r" }, + { mnemonic = "RES 0,(HL)", category = "RES_b_hl" }, + { mnemonic = "RES 0,A", category = "RES_b_r" }, + { mnemonic = "RES 1,B", category = "RES_b_r" }, + { mnemonic = "RES 1,C", category = "RES_b_r" }, + { mnemonic = "RES 1,D", category = "RES_b_r" }, + { mnemonic = "RES 1,E", category = "RES_b_r" }, + { mnemonic = "RES 1,H", category = "RES_b_r" }, + { mnemonic = "RES 1,L", category = "RES_b_r" }, + { mnemonic = "RES 1,(HL)", category = "RES_b_hl" }, + { mnemonic = "RES 1,A", category = "RES_b_r" }, + { mnemonic = "RES 2,B", category = "RES_b_r" }, + { mnemonic = "RES 2,C", category = "RES_b_r" }, + { mnemonic = "RES 2,D", category = "RES_b_r" }, + { mnemonic = "RES 2,E", category = "RES_b_r" }, + { mnemonic = "RES 2,H", category = "RES_b_r" }, + { mnemonic = "RES 2,L", category = "RES_b_r" }, + { mnemonic = "RES 2,(HL)", category = "RES_b_hl" }, + { mnemonic = "RES 2,A", category = "RES_b_r" }, + { mnemonic = "RES 3,B", category = "RES_b_r" }, + { mnemonic = "RES 3,C", category = "RES_b_r" }, + { mnemonic = "RES 3,D", category = "RES_b_r" }, + { mnemonic = "RES 3,E", category = "RES_b_r" }, + { mnemonic = "RES 3,H", category = "RES_b_r" }, + { mnemonic = "RES 3,L", category = "RES_b_r" }, + { mnemonic = "RES 3,(HL)", category = "RES_b_hl" }, + { mnemonic = "RES 3,A", category = "RES_b_r" }, + { mnemonic = "RES 4,B", category = "RES_b_r" }, + { mnemonic = "RES 4,C", category = "RES_b_r" }, + { mnemonic = "RES 4,D", category = "RES_b_r" }, + { mnemonic = "RES 4,E", category = "RES_b_r" }, + { mnemonic = "RES 4,H", category = "RES_b_r" }, + { mnemonic = "RES 4,L", category = "RES_b_r" }, + { mnemonic = "RES 4,(HL)", category = "RES_b_hl" }, + { mnemonic = "RES 4,A", category = "RES_b_r" }, + { mnemonic = "RES 5,B", category = "RES_b_r" }, + { mnemonic = "RES 5,C", category = "RES_b_r" }, + { mnemonic = "RES 5,D", category = "RES_b_r" }, + { mnemonic = "RES 5,E", category = "RES_b_r" }, + { mnemonic = "RES 5,H", category = "RES_b_r" }, + { mnemonic = "RES 5,L", category = "RES_b_r" }, + { mnemonic = "RES 5,(HL)", category = "RES_b_hl" }, + { mnemonic = "RES 5,A", category = "RES_b_r" }, + { mnemonic = "RES 6,B", category = "RES_b_r" }, + { mnemonic = "RES 6,C", category = "RES_b_r" }, + { mnemonic = "RES 6,D", category = "RES_b_r" }, + { mnemonic = "RES 6,E", category = "RES_b_r" }, + { mnemonic = "RES 6,H", category = "RES_b_r" }, + { mnemonic = "RES 6,L", category = "RES_b_r" }, + { mnemonic = "RES 6,(HL)", category = "RES_b_hl" }, + { mnemonic = "RES 6,A", category = "RES_b_r" }, + { mnemonic = "RES 7,B", category = "RES_b_r" }, + { mnemonic = "RES 7,C", category = "RES_b_r" }, + { mnemonic = "RES 7,D", category = "RES_b_r" }, + { mnemonic = "RES 7,E", category = "RES_b_r" }, + { mnemonic = "RES 7,H", category = "RES_b_r" }, + { mnemonic = "RES 7,L", category = "RES_b_r" }, + { mnemonic = "RES 7,(HL)", category = "RES_b_hl" }, + { mnemonic = "RES 7,A", category = "RES_b_r" }, + { mnemonic = "SET 0,B", category = "SET_b_r" }, + { mnemonic = "SET 0,C", category = "SET_b_r" }, + { mnemonic = "SET 0,D", category = "SET_b_r" }, + { mnemonic = "SET 0,E", category = "SET_b_r" }, + { mnemonic = "SET 0,H", category = "SET_b_r" }, + { mnemonic = "SET 0,L", category = "SET_b_r" }, + { mnemonic = "SET 0,(HL)", category = "SET_b_hl" }, + { mnemonic = "SET 0,A", category = "SET_b_r" }, + { mnemonic = "SET 1,B", category = "SET_b_r" }, + { mnemonic = "SET 1,C", category = "SET_b_r" }, + { mnemonic = "SET 1,D", category = "SET_b_r" }, + { mnemonic = "SET 1,E", category = "SET_b_r" }, + { mnemonic = "SET 1,H", category = "SET_b_r" }, + { mnemonic = "SET 1,L", category = "SET_b_r" }, + { mnemonic = "SET 1,(HL)", category = "SET_b_hl" }, + { mnemonic = "SET 1,A", category = "SET_b_r" }, + { mnemonic = "SET 2,B", category = "SET_b_r" }, + { mnemonic = "SET 2,C", category = "SET_b_r" }, + { mnemonic = "SET 2,D", category = "SET_b_r" }, + { mnemonic = "SET 2,E", category = "SET_b_r" }, + { mnemonic = "SET 2,H", category = "SET_b_r" }, + { mnemonic = "SET 2,L", category = "SET_b_r" }, + { mnemonic = "SET 2,(HL)", category = "SET_b_hl" }, + { mnemonic = "SET 2,A", category = "SET_b_r" }, + { mnemonic = "SET 3,B", category = "SET_b_r" }, + { mnemonic = "SET 3,C", category = "SET_b_r" }, + { mnemonic = "SET 3,D", category = "SET_b_r" }, + { mnemonic = "SET 3,E", category = "SET_b_r" }, + { mnemonic = "SET 3,H", category = "SET_b_r" }, + { mnemonic = "SET 3,L", category = "SET_b_r" }, + { mnemonic = "SET 3,(HL)", category = "SET_b_hl" }, + { mnemonic = "SET 3,A", category = "SET_b_r" }, + { mnemonic = "SET 4,B", category = "SET_b_r" }, + { mnemonic = "SET 4,C", category = "SET_b_r" }, + { mnemonic = "SET 4,D", category = "SET_b_r" }, + { mnemonic = "SET 4,E", category = "SET_b_r" }, + { mnemonic = "SET 4,H", category = "SET_b_r" }, + { mnemonic = "SET 4,L", category = "SET_b_r" }, + { mnemonic = "SET 4,(HL)", category = "SET_b_hl" }, + { mnemonic = "SET 4,A", category = "SET_b_r" }, + { mnemonic = "SET 5,B", category = "SET_b_r" }, + { mnemonic = "SET 5,C", category = "SET_b_r" }, + { mnemonic = "SET 5,D", category = "SET_b_r" }, + { mnemonic = "SET 5,E", category = "SET_b_r" }, + { mnemonic = "SET 5,H", category = "SET_b_r" }, + { mnemonic = "SET 5,L", category = "SET_b_r" }, + { mnemonic = "SET 5,(HL)", category = "SET_b_hl" }, + { mnemonic = "SET 5,A", category = "SET_b_r" }, + { mnemonic = "SET 6,B", category = "SET_b_r" }, + { mnemonic = "SET 6,C", category = "SET_b_r" }, + { mnemonic = "SET 6,D", category = "SET_b_r" }, + { mnemonic = "SET 6,E", category = "SET_b_r" }, + { mnemonic = "SET 6,H", category = "SET_b_r" }, + { mnemonic = "SET 6,L", category = "SET_b_r" }, + { mnemonic = "SET 6,(HL)", category = "SET_b_hl" }, + { mnemonic = "SET 6,A", category = "SET_b_r" }, + { mnemonic = "SET 7,B", category = "SET_b_r" }, + { mnemonic = "SET 7,C", category = "SET_b_r" }, + { mnemonic = "SET 7,D", category = "SET_b_r" }, + { mnemonic = "SET 7,E", category = "SET_b_r" }, + { mnemonic = "SET 7,H", category = "SET_b_r" }, + { mnemonic = "SET 7,L", category = "SET_b_r" }, + { mnemonic = "SET 7,(HL)", category = "SET_b_hl" }, + { mnemonic = "SET 7,A", category = "SET_b_r" }, +] + +[categories] +LD_r_r = { kind = "load_8b" } +LD_r_n = { kind = "load_8b" } +LD_r_hl = { kind = "load_8b" } +LD_hl_r = { kind = "load_8b" } +LD_hl_n = { kind = "load_8b" } +LD_a_bc = { kind = "load_8b" } +LD_a_de = { kind = "load_8b" } +LD_bc_a = { kind = "load_8b" } +LD_de_a = { kind = "load_8b" } +LD_a_nn = { kind = "load_8b" } +LD_nn_a = { kind = "load_8b" } +LDH_a_c = { kind = "load_8b" } +LDH_c_a = { kind = "load_8b" } +LDH_a_n = { kind = "load_8b" } +LDH_n_a = { kind = "load_8b" } +LD_a_hld = { kind = "load_8b" } +LD_hld_a = { kind = "load_8b" } +LD_a_hli = { kind = "load_8b" } +LD_hli_a = { kind = "load_8b" } +LD_rr_nn = { kind = "load_16b" } +LD_nn_sp = { kind = "load_16b" } +LD_sp_hl = { kind = "load_16b" } +LD_hl_sp_e = { kind = "load_16b" } +PUSH_rr = { kind = "load_16b" } +POP_rr = { kind = "load_16b" } +ADD_r = { kind = "alu_8b" } +ADD_hl = { kind = "alu_8b" } +ADD_n = { kind = "alu_8b" } +ADC_r = { kind = "alu_8b" } +ADC_hl = { kind = "alu_8b" } +ADC_n = { kind = "alu_8b" } +SUB_r = { kind = "alu_8b" } +SUB_hl = { kind = "alu_8b" } +SUB_n = { kind = "alu_8b" } +SBC_r = { kind = "alu_8b" } +SBC_hl = { kind = "alu_8b" } +SBC_n = { kind = "alu_8b" } +CP_r = { kind = "alu_8b" } +CP_hl = { kind = "alu_8b" } +CP_n = { kind = "alu_8b" } +INC_r = { kind = "alu_8b" } +INC_hl = { kind = "alu_8b" } +DEC_r = { kind = "alu_8b" } +DEC_hl = { kind = "alu_8b" } +AND_r = { kind = "alu_8b" } +AND_hl = { kind = "alu_8b" } +AND_n = { kind = "alu_8b" } +OR_r = { kind = "alu_8b" } +OR_hl = { kind = "alu_8b" } +OR_n = { kind = "alu_8b" } +XOR_r = { kind = "alu_8b" } +XOR_hl = { kind = "alu_8b" } +XOR_n = { kind = "alu_8b" } +CCF = { kind = "alu_8b" } +SCF = { kind = "alu_8b" } +DAA = { kind = "alu_8b" } +CPL = { kind = "alu_8b" } +INC_rr = { kind = "alu_16b" } +DEC_rr = { kind = "alu_16b" } +ADD_hl_rr = { kind = "alu_16b" } +ADD_sp_e = { kind = "alu_16b" } +CB = { kind = "rotate_shift_bit" } +RLCA = { kind = "rotate_shift_bit" } +RRCA = { kind = "rotate_shift_bit" } +RLA = { kind = "rotate_shift_bit" } +RRA = { kind = "rotate_shift_bit" } +RLC_r = { kind = "rotate_shift_bit" } +RLC_hl = { kind = "rotate_shift_bit" } +RRC_r = { kind = "rotate_shift_bit" } +RRC_hl = { kind = "rotate_shift_bit" } +RL_r = { kind = "rotate_shift_bit" } +RL_hl = { kind = "rotate_shift_bit" } +RR_r = { kind = "rotate_shift_bit" } +RR_hl = { kind = "rotate_shift_bit" } +SLA_r = { kind = "rotate_shift_bit" } +SLA_hl = { kind = "rotate_shift_bit" } +SRA_r = { kind = "rotate_shift_bit" } +SRA_hl = { kind = "rotate_shift_bit" } +SWAP_r = { kind = "rotate_shift_bit" } +SWAP_hl = { kind = "rotate_shift_bit" } +SRL_r = { kind = "rotate_shift_bit" } +SRL_hl = { kind = "rotate_shift_bit" } +BIT_b_r = { kind = "rotate_shift_bit" } +BIT_b_hl = { kind = "rotate_shift_bit" } +RES_b_r = { kind = "rotate_shift_bit" } +RES_b_hl = { kind = "rotate_shift_bit" } +SET_b_r = { kind = "rotate_shift_bit" } +SET_b_hl = { kind = "rotate_shift_bit" } +JP = { kind = "control" } +JP_hl = { kind = "control" } +JP_cc = { kind = "control" } +JR = { kind = "control" } +JR_cc = { kind = "control" } +CALL = { kind = "control" } +CALL_cc = { kind = "control" } +RET = { kind = "control" } +RET_cc = { kind = "control" } +RETI = { kind = "control" } +RST = { kind = "control" } +HALT = { kind = "misc" } +STOP = { kind = "misc" } +DI = { kind = "misc" } +EI = { kind = "misc" } +NOP = { kind = "misc" } +undefined = { kind = "undefined" } diff --git a/preface.typ b/preface.typ new file mode 100644 index 0000000..1cfb6d6 --- /dev/null +++ b/preface.typ @@ -0,0 +1,102 @@ +#import "common.typ": * + +== Preface +#v(2em) + +#caveat[ + #text(17pt)[ + IMPORTANT: This document focuses at the moment on 1st and 2nd generation + devices (models before the Game Boy Color), and some hardware details are + very different in later generations. + + Be very careful if you make assumptions about later generation devices based + on this document! + ] +] + +#pagebreak() + +== How to read this document +#counter(heading).update((0, 0)) +#v(2em) + +#speculation[ + This is something that hasn't been verified, but would make a lot of sense. +] + +#caveat[ + This explains some caveat about this documentation that you should know. +] + +#warning[ + This is a warning about something. +] + +=== Formatting of numbers + +When a single bit is discussed in isolation, the value looks like this: #bit("0"), #bit("1"). + +Binary numbers are prefixed with #bin("") like this: #bin("0101101"), #bin("11011"), #bin("00000000"). Values are prefixed with zeroes when necessary, so the total number of digits always matches the number of digits in the value. + +Hexadecimal numbers are prefixed with #hex("") like this: #hex("1234"), #hex("DEADBEEF"), #hex("FF04"). Values are prefixed with zeroes when necessary, so the total number of characters always matches the number of nibbles in the value. + +Examples: + +#v(1em) + +#table( + columns: 4, + stroke: none, + [], [4-bit], [8-bit], [16-bit], + [Binary], bin("0101"), bin("10100101"), bin("0000101010100101"), + [Hexadecimal], hex("5"), hex("A5"), hex("0AA5") +) + +#pagebreak() +=== Register definitions + +#reg-figure( + caption: [#hex("1234") - This is a hardware register definition] +)[ + #reg-table( + [R/W-0], [R/W-1], [U-1], [R-0], [R-1], [R-x], [W-1], [U-0], + colspanx(2)[VALUE\<1:0\>], unimpl-bit(), colspanx(3)[BIGVAL\<7:5\>], [FLAG], unimpl-bit(), + [bit 7], [6], [5], [4], [3], [2], [1], [bit 0] + ) + #set align(left) + + #v(1em) + *Top row legend:* + + #grid( + columns: (1cm, 1fr), + gutter: 1em, + [*R*], [Bit can be read.], + [*W*], [Bit can be written. If the bit cannot be read, reading returns a constant value defined in the bit list of the register in question.], + [*U*], [Unimplemented bit. Writing has no effect, and reading returns a constant value defined in the bit list of the register in question.], + [*-n*], [Value after system reset: #bit("0"), #bit("1"), or x.], + [*#bit("1")*], [Bit is set.], + [*#bit("0")*], [Bit is cleared.], + [*x*], [Bit is unknown (e.g. depends on external things such as user input)] + ) + + #v(1em) + *Middle row legend:* + #tablex( + columns: 2, + align: center + horizon, + `VALUE<1:0>`, [Bits 1 and 0 of VALUE], + unimpl-bit(), [Unimplemented bit], + `BIGVAL<7:5>`, [Bits 7, 6, 5 of BIGVAL], + `FLAG`, [Single-bit value FLAG] + ) + + #v(1em) + *In this example:* + + - After system reset, VALUE is #bin("01"), BIGVAL is either #bin("010") or #bin("011"), FLAG is #bin("1"). + - Bits 5 and 0 are unimplemented. Bit 5 always returns #bit("1"), and bit 0 always returns #bit("0"). + - Both bits of VALUE can be read and written. When this register is written, bit 7 of the written value goes to bit 1 of VALUE. + - FLAG can only be written to, so reads return a value that is defined elsewhere. + - BIGVAL cannot be written to. Only bits 5-7 of BIGVAL are defined here, so look elsewhere for the low bits 0-4. +] diff --git a/third-party/UseLATEX.cmake b/third-party/UseLATEX.cmake deleted file mode 100644 index 85d32e8..0000000 --- a/third-party/UseLATEX.cmake +++ /dev/null @@ -1,2099 +0,0 @@ -# File: UseLATEX.cmake -# CMAKE commands to actually use the LaTeX compiler -# Version: 2.7.2 -# Author: Kenneth Moreland -# -# Copyright 2004, 2015 Sandia Corporation. -# Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive -# license for use of this work by or on behalf of the U.S. Government. -# -# This software is released under the BSD 3-Clause License. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its -# contributors may be used to endorse or promote products derived from this -# software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# -# The following function is defined: -# -# add_latex_document( -# [BIBFILES ] -# [INPUTS ] -# [IMAGE_DIRS ] -# [IMAGES ] -# [CONFIGURE ] -# [DEPENDS ] -# [MULTIBIB_NEWCITES ] -# [USE_BIBLATEX] -# [USE_INDEX] -# [INDEX_NAMES ] -# [USE_GLOSSARY] [USE_NOMENCL] -# [FORCE_PDF] [FORCE_DVI] [FORCE_HTML] -# [TARGET_NAME ] -# [INCLUDE_DIRECTORIES ] -# [EXCLUDE_FROM_ALL] -# [EXCLUDE_FROM_DEFAULTS]) -# Adds targets that compile . The latex output is placed -# in LATEX_OUTPUT_PATH or CMAKE_CURRENT_BINARY_DIR if the former is -# not set. The latex program is picky about where files are located, -# so all input files are copied from the source directory to the -# output directory. This includes the target tex file, any tex file -# listed with the INPUTS option, the bibliography files listed with -# the BIBFILES option, and any .cls, .bst, .clo, .sty, .ist, and .fd -# files found in the current source directory. Images found in the -# IMAGE_DIRS directories or listed by IMAGES are also copied to the -# output directory and converted to an appropriate format if necessary. -# Any tex files also listed with the CONFIGURE option are also processed -# with the CMake CONFIGURE_FILE command (with the @ONLY flag). Any file -# listed in CONFIGURE but not the target tex file or listed with INPUTS -# has no effect. DEPENDS can be used to specify generated files that are -# needed to compile the latex target. -# -# The following targets are made. The name prefix is based off of the -# base name of the tex file unless TARGET_NAME is specified. If -# TARGET_NAME is specified, then that name is used for the targets. -# -# name_dvi: Makes .dvi -# name_pdf: Makes .pdf using pdflatex. -# name_safepdf: Makes .pdf using ps2pdf. If using the -# default program arguments, this will ensure all fonts -# are embedded and no lossy compression has been -# performed on images. -# name_ps: Makes .ps -# name_html: Makes .html -# name_auxclean: Deletes .aux and other auxiliary files. -# This is sometimes necessary if a LaTeX error occurs -# and writes a bad aux file. Unlike the regular clean -# target, it does not delete other input files, such as -# converted images, to save time on the rebuild. -# -# Unless the EXCLUDE_FROM_ALL option is given, one of these targets -# is added to the ALL target and built by default. Which target is -# determined by the LATEX_DEFAULT_BUILD CMake variable. See the -# documentation of that variable for more details. -# -# Unless the EXCLUDE_FROM_DEFAULTS option is given, all these targets -# are added as dependencies to targets named dvi, pdf, safepdf, ps, -# html, and auxclean, respectively. -# -# USE_BIBLATEX enables the use of biblatex/biber as an alternative to -# bibtex. Bibtex remains the default if USE_BIBLATEX is not -# specified. -# -# If the argument USE_INDEX is given, then commands to build an index -# are made. If the argument INDEX_NAMES is given, an index file is -# generated for each name in this list. See the LaTeX package multind -# for more information about how to generate multiple indices. -# -# If the argument USE_GLOSSARY is given, then commands to -# build a glossary are made. If the argument MULTIBIB_NEWCITES is -# given, then additional bibtex calls are added to the build to -# support the extra auxiliary files created with the \newcite command -# in the multibib package. -# -# INCLUDE_DIRECTORIES provides a list of directories in which LaTeX -# should look for input files. It accepts both files relative to the -# binary directory and absolute paths. -# -# History: -# -# 2.7.2 Add CONFIGURE_DEPENDS option when globbing files to better detect -# when files in a directory change. This only happens for CMake 3.12 -# or better. -# -# 2.7.1 Fix issues with printing log on errors when there are spaces in the -# path. (Thanks to Peter Knowles.) -# -# Ignore LaTeX warning about the font shape series value `mc'. -# -# 2.7.0 Add INCLUDE_DIRECTORIES parameters. (Thanks to Eric Dönges.) -# -# 2.6.1 Fix issue with detecting long undefined reference warnings that -# LaTeX "helpfully" split across lines (and which fowled up our -# regex). -# -# 2.6.0 Skip image conversion targets that are not used when a force option -# is given. This helps prevent errors for missing conversion programs -# that are not needed. (Thanks to Martin Wetzel.) -# -# 2.5.0 Parse biber output for warnings. -# -# For regular bibtex, you get warnings about undefined references -# when you run latex. However, when using biber, biber itself prints -# out the said warning and latex sees nothing. Thus, when using biber -# the regular output is now suppressed and the log file is scanned -# for potential issues. -# -# 2.4.9 Use biblatex.cfg file if it exists and the USE_BIBLATEX option is ON. -# -# 2.4.8 Fix synctex issue with absolute paths not being converted. -# -# 2.4.7 Fix some issues with spaces in the path of the working directory where -# LaTeX is executed. -# -# 2.4.6 Fix parse issue with older versions of CMake. -# -# 2.4.5 Fix issues with files and paths containing spaces. -# -# 2.4.4 Improve error reporting message when LaTeX fails. -# -# When LaTeX fails, delete the output file, which is invalid. -# -# Add warnings for "missing characters." These usually mean that a -# non-ASCII character is in the document and will not be printed -# correctly. -# -# 2.4.3 Check for warnings from the natbib package. When using natbib, -# warnings for missing bibliography references look different. So -# far, natbib seems to be quiet unless something is important, so -# look for all natbib warnings. (We can change this later if -# necessary.) -# -# 2.4.2 Fix an issue where new versions of ImageMagick expect the order of -# options in command line execution of magick/convert. (See, for -# example, http://www.imagemagick.org/Usage/basics/#why.) -# -# 2.4.1 Add ability to dump LaTeX log file when using batch mode. Batch -# mode suppresses most output, often including error messages. To -# make sure critical error messages get displayed, show the full log -# on failures. -# -# 2.4.0 Remove "-r 600" from the default PDFTOPS_CONVERTER_FLAGS. The -r flag -# is available from the Poppler version of pdftops, but not the Xpdf -# version. -# -# Fix an issue with the flags for the different programs not being -# properly separated. -# -# Fix an issue on windows where the = character is not allowed for -# ps2pdf arguments. -# -# Change default arguments for latex and pdflatex commands. Makes the -# output more quiet and prints out the file/line where errors occur. -# (Thanks to Nikos Koukis.) -# -# After a LaTeX build, check the log file for warnings that are -# indicative of problems with the build. -# -# Remove support for latex2html. Instead, use the htlatex program. -# This is now part of TeX Live and most other distributions. It also -# behaves much more like the other LaTeX programs. Also fixed some -# nasty issues with the htlatex arguments. -# -# 2.3.2 Declare LaTeX input files as sources for targets so that they show -# up in IDEs like QtCreator. -# -# Fix issue where main tex files in subdirectories were creating -# invalid targets for building HTML. Just disable the HTML targets in -# this case. -# -# 2.3.1 Support use of magick command instead of convert command for -# ImageMagick 7. -# -# 2.3.0 Add USE_BIBLATEX option to support the biblatex package, which -# requires using the program biber as a replacement for bibtex -# (thanks to David Tracey). -# -# 2.2.1 Add STRINGS property to LATEX_DEFAULT_BUILD to make it easier to -# select the default build in the CMake GUI. -# -# 2.2.0 Add TARGET_NAME option. -# -# 2.1.1 Support for finding bmp, ppm, and other image files. -# -# 2.1.0 Fix an error where the pdf target and others were defined multiple -# times if UseLATEX.cmake was included multiple times. -# -# Added INDEX_NAMES option to support multiple indexes in a single -# document from the multind package (thanks to Dan Lipsa). -# -# 2.0.0 First major revision of UseLATEX.cmake updates to more recent features -# of CMake and some non-backward compatible changes. -# -# Changed all function and macro names to lower case. CMake's identifiers -# are case insensitive, but the convention moved from all upper case to -# all lower case somewhere around the release of CMake 2. (The original -# version of UseLATEX.cmake predates that.) -# -# Remove condition matching in if statements. They are no longer necessary -# and are even discouraged (because else clauses get confusing). -# -# Use "new" features available in CMake such as list and argument parsing. -# -# Remove some code that has been deprecated for a while. -# -# Mark variables for compiler and converter executables as advanced to -# match the more conventional CMake behavior. -# -# Changed how default builds are specified and add the ability to force -# a particular build. -# -# Made the base targets (pdf, dvi, etc.) global. add_latex_document -# always mangles its target names and these base targets depend on -# the targets with mangled names. -# -# 1.10.5 Fix for Window's convert check (thanks to Martin Baute). -# -# 1.10.4 Copy font files to binary directory for packages that come with -# their own fonts. -# -# 1.10.3 Check for Windows version of convert being used instead of -# ImageMagick's version (thanks to Martin Baute). -# -# 1.10.2 Use htlatex as a fallback when latex2html is not available (thanks -# to Tomasz Grzegurzko). -# -# 1.10.1 Make convert program mandatory only if actually used (thanks to -# Julien Schueller). -# -# 1.10.0 Added NO_DEFAULT and DEFAULT_PS options. -# Fixed issue with cleaning files for LaTeX documents originating in -# a subdirectory. -# -# 1.9.6 Fixed problem with LATEX_SMALL_IMAGES. -# Strengthened check to make sure the output directory does not contain -# the source files. -# -# 1.9.5 Add support for image types not directly supported by either latex -# or pdflatex. (Thanks to Jorge Gerardo Pena Pastor for SVG support.) -# -# 1.9.4 Fix issues with filenames containing multiple periods. -# -# 1.9.3 Hide some variables that are now cached but should not show up in -# the ccmake list of variables. -# -# 1.9.2 Changed MACRO declarations to FUNCTION declarations. The better -# FUNCTION scoping will hopefully avoid some common but subtle bugs. -# This implicitly increases the minimum CMake version to 4.6 (although -# I honestly only test it with the latest 4.8 version). -# -# Since we are updating the minimum CMake version, I'm going to start -# using the builtin LIST commands that are now available. -# -# Favor using pdftops from the Poppler package to convert from pdf to -# eps. It does a much better job than ImageMagick or ghostscript. -# -# 1.9.1 Fixed typo that caused the LATEX_SMALL_IMAGES option to fail to -# activate. -# -# 1.9.0 Add support for the multibib package (thanks to Antonio LaTorre). -# -# 1.8.2 Fix corner case when an argument name was also a variable containing -# the text of an argument. In this case, the CMake IF was matching -# the argument text with the contents of the variable with the same -# argument name. -# -# 1.8.1 Fix problem where ps2pdf was not getting the appropriate arguments. -# -# 1.8.0 Add support for synctex. -# -# 1.7.7 Support calling xindy when making glossaries. -# -# Improved make clean support. -# -# 1.7.6 Add support for the nomencl package (thanks to Myles English). -# -# 1.7.5 Fix issue with bibfiles being copied two different ways, which causes -# Problems with dependencies (thanks to Edwin van Leeuwen). -# -# 1.7.4 Added the DEFAULT_SAFEPDF option (thanks to Raymond Wan). -# -# Added warnings when image directories are not found (and were -# probably not given relative to the source directory). -# -# 1.7.3 Fix some issues with interactions between makeglossaries and bibtex -# (thanks to Mark de Wever). -# -# 1.7.2 Use ps2pdf to convert eps to pdf to get around the problem with -# ImageMagick dropping the bounding box (thanks to Lukasz Lis). -# -# 1.7.1 Fixed some dependency issues. -# -# 1.7.0 Added DEPENDS options (thanks to Theodore Papadopoulo). -# -# 1.6.1 Ported the makeglossaries command to CMake and embedded the port -# into UseLATEX.cmake. -# -# 1.6.0 Allow the use of the makeglossaries command. Thanks to Oystein -# S. Haaland for the patch. -# -# 1.5.0 Allow any type of file in the INPUTS lists, not just tex file -# (suggested by Eric Noulard). As a consequence, the ability to -# specify tex files without the .tex extension is removed. The removed -# function is of dubious value anyway. -# -# When copying input files, skip over any file that exists in the -# binary directory but does not exist in the source directory with the -# assumption that these files were added by some other mechanism. I -# find this useful when creating large documents with multiple -# chapters that I want to build separately (for speed) as I work on -# them. I use the same boilerplate as the starting point for all -# and just copy it with different configurations. This was what the -# separate ADD_LATEX_DOCUMENT method was supposed to originally be for. -# Since its external use is pretty much deprecated, I removed that -# documentation. -# -# 1.4.1 Copy .sty files along with the other class and package files. -# -# 1.4.0 Added a MANGLE_TARGET_NAMES option that will mangle the target names. -# -# Fixed problem with copying bib files that became apparent with -# CMake 2.4. -# -# 1.3.0 Added a LATEX_OUTPUT_PATH variable that allows you or the user to -# specify where the built latex documents to go. This is especially -# handy if you want to do in-source builds. -# -# Removed the ADD_LATEX_IMAGES macro and absorbed the functionality -# into ADD_LATEX_DOCUMENT. The old interface was always kind of -# clunky anyway since you had to specify the image directory in both -# places. It also made supporting LATEX_OUTPUT_PATH problematic. -# -# Added support for jpeg files. -# -# 1.2.0 Changed the configuration options yet again. Removed the NO_CONFIGURE -# Replaced it with a CONFIGURE option that lists input files for which -# configure should be run. -# -# The pdf target no longer depends on the dvi target. This allows you -# to build latex documents that require pdflatex. Also added an option -# to make the pdf target the default one. -# -# 1.1.1 Added the NO_CONFIGURE option. The @ character can be used when -# specifying table column separators. If two or more are used, then -# will incorrectly substitute them. -# -# 1.1.0 Added ability include multiple bib files. Added ability to do copy -# sub-tex files for multipart tex files. -# -# 1.0.0 If both ps and pdf type images exist, just copy the one that -# matches the current render mode. Replaced a bunch of STRING -# commands with GET_FILENAME_COMPONENT commands that were made to do -# the desired function. -# -# 0.4.0 First version posted to CMake Wiki. -# - -if(__USE_LATEX_INCLUDED) - return() -endif() -set(__USE_LATEX_INCLUDED TRUE) - -############################################################################# -# Find the location of myself while originally executing. If you do this -# inside of a macro, it will recode where the macro was invoked. -############################################################################# -set(LATEX_USE_LATEX_LOCATION ${CMAKE_CURRENT_LIST_FILE} - CACHE INTERNAL "Location of UseLATEX.cmake file." FORCE - ) - -############################################################################# -# Generic helper functions -############################################################################# - -include(CMakeParseArguments) - -function(latex_list_contains var value) - set(input_list ${ARGN}) - list(FIND input_list "${value}" index) - if(index GREATER -1) - set(${var} TRUE PARENT_SCOPE) - else() - set(${var} PARENT_SCOPE) - endif() -endfunction(latex_list_contains) - -# Match the contents of a file to a regular expression. -function(latex_file_match variable filename regexp default) - # The FILE STRINGS command would be a bit better, but I'm not totally sure - # the match will always be to a whole line, and I don't want to break things. - file(READ ${filename} file_contents) - string(REGEX MATCHALL "${regexp}" - match_result ${file_contents} - ) - if(match_result) - set(${variable} "${match_result}" PARENT_SCOPE) - else() - set(${variable} "${default}" PARENT_SCOPE) - endif() -endfunction(latex_file_match) - -# A version of GET_FILENAME_COMPONENT that treats extensions after the last -# period rather than the first. To the best of my knowledge, all filenames -# typically used by LaTeX, including image files, have small extensions -# after the last dot. -function(latex_get_filename_component varname filename type) - set(result) - if("${type}" STREQUAL "NAME_WE") - get_filename_component(name ${filename} NAME) - string(REGEX REPLACE "\\.[^.]*\$" "" result "${name}") - elseif("${type}" STREQUAL "EXT") - get_filename_component(name ${filename} NAME) - string(REGEX MATCH "\\.[^.]*\$" result "${name}") - else() - get_filename_component(result ${filename} ${type}) - endif() - set(${varname} "${result}" PARENT_SCOPE) -endfunction(latex_get_filename_component) - -############################################################################# -# Functions that perform processing during a LaTeX build. -############################################################################# -function(latex_execute_latex) - if(NOT LATEX_WORKING_DIRECTORY) - message(SEND_ERROR "Need to define LATEX_WORKING_DIRECTORY") - endif() - - if(NOT LATEX_FULL_COMMAND) - message(SEND_ERROR "Need to define LATEX_FULL_COMMAND") - endif() - - if(NOT LATEX_OUTPUT_FILE) - message(SEND_ERROR "Need to define LATEX_OUTPUT_FILE") - endif() - - if(NOT LATEX_LOG_FILE) - message(SEND_ERROR "Need to define LATEX_LOG_FILE") - endif() - - set(full_command_original "${LATEX_FULL_COMMAND}") - - # Chose the native method for parsing command arguments. Newer versions of - # CMake allow you to just use NATIVE_COMMAND. - if (CMAKE_VERSION VERSION_GREATER 3.8) - set(separate_arguments_mode NATIVE_COMMAND) - else() - if (WIN32) - set(separate_arguments_mode WINDOWS_COMMAND) - else() - set(separate_arguments_mode UNIX_COMMAND) - endif() - endif() - - # Preps variables for use in execute_process. - # Even though we expect LATEX_WORKING_DIRECTORY to have a single "argument," - # we also want to make sure that we strip out any escape characters that can - # foul up the WORKING_DIRECTORY argument. - separate_arguments(LATEX_FULL_COMMAND UNIX_COMMAND "${LATEX_FULL_COMMAND}") - separate_arguments(LATEX_WORKING_DIRECTORY_SEP UNIX_COMMAND "${LATEX_WORKING_DIRECTORY}") - - execute_process( - COMMAND ${LATEX_FULL_COMMAND} - WORKING_DIRECTORY "${LATEX_WORKING_DIRECTORY_SEP}" - RESULT_VARIABLE execute_result - OUTPUT_VARIABLE ignore - ERROR_VARIABLE ignore - ) - - if(NOT ${execute_result} EQUAL 0) - # LaTeX tends to write a file when a failure happens. Delete that file so - # that LaTeX will run again. - separate_arguments(LATEX_OUTPUT_FILE_SEP UNIX_COMMAND "${LATEX_OUTPUT_FILE}") - file(REMOVE "${LATEX_WORKING_DIRECTORY_SEP}/${LATEX_OUTPUT_FILE_SEP}") - - message("\n\nLaTeX command failed") - message("${full_command_original}") - message("Log output:") - separate_arguments(LATEX_LOG_FILE_SEP UNIX_COMMAND "${LATEX_LOG_FILE}") - file(READ "${LATEX_WORKING_DIRECTORY_SEP}/${LATEX_LOG_FILE_SEP}" log_output) - message("${log_output}") - message(FATAL_ERROR "Executed LaTeX, but LaTeX returned an error.") - endif() -endfunction(latex_execute_latex) - -function(latex_makeglossaries) - # This is really a bare bones port of the makeglossaries perl script into - # CMake scripting. - message("**************************** In makeglossaries") - if(NOT LATEX_TARGET) - message(SEND_ERROR "Need to define LATEX_TARGET") - endif() - - set(aux_file ${LATEX_TARGET}.aux) - - if(NOT EXISTS ${aux_file}) - message(SEND_ERROR "${aux_file} does not exist. Run latex on your target file.") - endif() - - latex_file_match(newglossary_lines ${aux_file} - "@newglossary[ \t]*{([^}]*)}{([^}]*)}{([^}]*)}{([^}]*)}" - "@newglossary{main}{glg}{gls}{glo}" - ) - - latex_file_match(istfile_line ${aux_file} - "@istfilename[ \t]*{([^}]*)}" - "@istfilename{${LATEX_TARGET}.ist}" - ) - string(REGEX REPLACE "@istfilename[ \t]*{([^}]*)}" "\\1" - istfile ${istfile_line} - ) - - string(REGEX MATCH ".*\\.xdy" use_xindy "${istfile}") - if(use_xindy) - message("*************** Using xindy") - if(NOT XINDY_COMPILER) - message(SEND_ERROR "Need to define XINDY_COMPILER") - endif() - else() - message("*************** Using makeindex") - if(NOT MAKEINDEX_COMPILER) - message(SEND_ERROR "Need to define MAKEINDEX_COMPILER") - endif() - endif() - - foreach(newglossary ${newglossary_lines}) - string(REGEX REPLACE - "@newglossary[ \t]*{([^}]*)}{([^}]*)}{([^}]*)}{([^}]*)}" - "\\1" glossary_name ${newglossary} - ) - string(REGEX REPLACE - "@newglossary[ \t]*{([^}]*)}{([^}]*)}{([^}]*)}{([^}]*)}" - "${LATEX_TARGET}.\\2" glossary_log ${newglossary} - ) - string(REGEX REPLACE - "@newglossary[ \t]*{([^}]*)}{([^}]*)}{([^}]*)}{([^}]*)}" - "${LATEX_TARGET}.\\3" glossary_out ${newglossary} - ) - string(REGEX REPLACE - "@newglossary[ \t]*{([^}]*)}{([^}]*)}{([^}]*)}{([^}]*)}" - "${LATEX_TARGET}.\\4" glossary_in ${newglossary} - ) - - if(use_xindy) - latex_file_match(xdylanguage_line ${aux_file} - "@xdylanguage[ \t]*{${glossary_name}}{([^}]*)}" - "@xdylanguage{${glossary_name}}{english}" - ) - string(REGEX REPLACE - "@xdylanguage[ \t]*{${glossary_name}}{([^}]*)}" - "\\1" - language - ${xdylanguage_line} - ) - # What crazy person makes a LaTeX index generator that uses different - # identifiers for language than babel (or at least does not support - # the old ones)? - if(${language} STREQUAL "frenchb") - set(language "french") - elseif(${language} MATCHES "^n?germanb?$") - set(language "german") - elseif(${language} STREQUAL "magyar") - set(language "hungarian") - elseif(${language} STREQUAL "lsorbian") - set(language "lower-sorbian") - elseif(${language} STREQUAL "norsk") - set(language "norwegian") - elseif(${language} STREQUAL "portuges") - set(language "portuguese") - elseif(${language} STREQUAL "russianb") - set(language "russian") - elseif(${language} STREQUAL "slovene") - set(language "slovenian") - elseif(${language} STREQUAL "ukraineb") - set(language "ukrainian") - elseif(${language} STREQUAL "usorbian") - set(language "upper-sorbian") - endif() - if(language) - set(language_flags "-L ${language}") - else() - set(language_flags "") - endif() - - latex_file_match(codepage_line ${aux_file} - "@gls@codepage[ \t]*{${glossary_name}}{([^}]*)}" - "@gls@codepage{${glossary_name}}{utf}" - ) - string(REGEX REPLACE - "@gls@codepage[ \t]*{${glossary_name}}{([^}]*)}" - "\\1" - codepage - ${codepage_line} - ) - if(codepage) - set(codepage_flags "-C ${codepage}") - else() - # Ideally, we would check that the language is compatible with the - # default codepage, but I'm hoping that distributions will be smart - # enough to specify their own codepage. I know, it's asking a lot. - set(codepage_flags "") - endif() - - message("${XINDY_COMPILER} ${MAKEGLOSSARIES_COMPILER_ARGS} ${language_flags} ${codepage_flags} -I xindy -M ${glossary_name} -t ${glossary_log} -o ${glossary_out} ${glossary_in}" - ) - exec_program(${XINDY_COMPILER} - ARGS ${MAKEGLOSSARIES_COMPILER_ARGS} - ${language_flags} - ${codepage_flags} - -I xindy - -M ${glossary_name} - -t ${glossary_log} - -o ${glossary_out} - ${glossary_in} - OUTPUT_VARIABLE xindy_output - ) - message("${xindy_output}") - - # So, it is possible (perhaps common?) for aux files to specify a - # language and codepage that are incompatible with each other. Check - # for that condition, and if it happens run again with the default - # codepage. - if("${xindy_output}" MATCHES "^Cannot locate xindy module for language (.+) in codepage (.+)\\.$") - message("*************** Retrying xindy with default codepage.") - exec_program(${XINDY_COMPILER} - ARGS ${MAKEGLOSSARIES_COMPILER_ARGS} - ${language_flags} - -I xindy - -M ${glossary_name} - -t ${glossary_log} - -o ${glossary_out} - ${glossary_in} - ) - endif() - - else() - message("${MAKEINDEX_COMPILER} ${MAKEGLOSSARIES_COMPILER_ARGS} -s ${istfile} -t ${glossary_log} -o ${glossary_out} ${glossary_in}") - exec_program(${MAKEINDEX_COMPILER} ARGS ${MAKEGLOSSARIES_COMPILER_ARGS} - -s ${istfile} -t ${glossary_log} -o ${glossary_out} ${glossary_in} - ) - endif() - - endforeach(newglossary) -endfunction(latex_makeglossaries) - -function(latex_makenomenclature) - message("**************************** In makenomenclature") - if(NOT LATEX_TARGET) - message(SEND_ERROR "Need to define LATEX_TARGET") - endif() - - if(NOT MAKEINDEX_COMPILER) - message(SEND_ERROR "Need to define MAKEINDEX_COMPILER") - endif() - - set(nomencl_out ${LATEX_TARGET}.nls) - set(nomencl_in ${LATEX_TARGET}.nlo) - - exec_program(${MAKEINDEX_COMPILER} ARGS ${MAKENOMENCLATURE_COMPILER_ARGS} - ${nomencl_in} -s "nomencl.ist" -o ${nomencl_out} - ) -endfunction(latex_makenomenclature) - -function(latex_correct_synctex) - message("**************************** In correct SyncTeX") - if(NOT LATEX_TARGET) - message(SEND_ERROR "Need to define LATEX_TARGET") - endif() - - if(NOT GZIP) - message(SEND_ERROR "Need to define GZIP") - endif() - - if(NOT LATEX_SOURCE_DIRECTORY) - message(SEND_ERROR "Need to define LATEX_SOURCE_DIRECTORY") - endif() - - if(NOT LATEX_BINARY_DIRECTORY) - message(SEND_ERROR "Need to define LATEX_BINARY_DIRECTORY") - endif() - message("${LATEX_BINARY_DIRECTORY}") - message("${LATEX_SOURCE_DIRECTORY}") - - set(synctex_file ${LATEX_BINARY_DIRECTORY}/${LATEX_TARGET}.synctex) - set(synctex_file_gz ${synctex_file}.gz) - - if(EXISTS ${synctex_file_gz}) - - message("Making backup of synctex file.") - configure_file(${synctex_file_gz} ${synctex_file}.bak.gz COPYONLY) - - message("Uncompressing synctex file.") - exec_program(${GZIP} - ARGS --decompress ${synctex_file_gz} - ) - - message("Reading synctex file.") - file(READ ${synctex_file} synctex_data) - - message("Replacing output paths with input paths.") - foreach(extension tex cls bst clo sty ist fd) - # Relative paths - string(REGEX REPLACE - "(Input:[0-9]+:)([^/\n][^\n]\\.${extension}*)" - "\\1${LATEX_SOURCE_DIRECTORY}/\\2" - synctex_data - "${synctex_data}" - ) - - # Absolute paths - string(REGEX REPLACE - "(Input:[0-9]+:)${LATEX_BINARY_DIRECTORY}([^\n]*\\.${extension})" - "\\1${LATEX_SOURCE_DIRECTORY}\\2" - synctex_data - "${synctex_data}" - ) - endforeach(extension) - - message("Writing synctex file.") - file(WRITE ${synctex_file} "${synctex_data}") - - message("Compressing synctex file.") - exec_program(${GZIP} - ARGS ${synctex_file} - ) - - else() - - message(SEND_ERROR "File ${synctex_file_gz} not found. Perhaps synctex is not supported by your LaTeX compiler.") - - endif() - -endfunction(latex_correct_synctex) - -function(latex_check_important_warnings) - # Check for biber warnings/errors if that was run - set(bib_log_file ${LATEX_TARGET}.blg) - if(EXISTS ${bib_log_file}) - file(READ ${bib_log_file} bib_log) - if(bib_log MATCHES "INFO - This is Biber") - message("\nChecking ${bib_log_file} for Biber warnings/errors.") - - string(REGEX MATCHALL - "[A-Z]+ - [^\n]*" - biber_messages - "${bib_log}") - - set(found_error) - foreach(message ${biber_messages}) - if(NOT message MATCHES "^INFO - ") - set(found_error TRUE) - message("${message}") - endif() - endforeach(message) - - if(found_error) - latex_get_filename_component(log_file_path ${bib_log_file} ABSOLUTE) - message("\nConsult ${log_file_path} for more information on Biber output.") - else() - message("No known important Biber output found.") - endif(found_error) - else() # Biber output not in log file - message("Skipping biber checks (biber not used)") - endif() - else() # No bib log file - message("Skipping bibliography checks (not run)") - endif() - - set(log_file ${LATEX_TARGET}.log) - - message("\nChecking ${log_file} for important warnings.") - if(NOT LATEX_TARGET) - message(SEND_ERROR "Need to define LATEX_TARGET") - endif() - - if(NOT EXISTS ${log_file}) - message("Could not find log file: ${log_file}") - return() - endif() - - set(found_error) - - file(READ ${log_file} log) - - # Check for declared LaTeX warnings - string(REGEX MATCHALL - "\nLaTeX Warning:[^\n]*" - latex_warnings - "${log}") - # Ignore warnings considered harmless - list(FILTER latex_warnings EXCLUDE REGEX - "LaTeX Warning: Font shape declaration has incorrect series value `mc'." - ) - if(latex_warnings) - set(found_error TRUE) - message("\nFound declared LaTeX warnings.") - foreach(warning ${latex_warnings}) - string(STRIP "${warning}" warning_no_newline) - message("${warning_no_newline}") - endforeach(warning) - endif() - - # Check for natbib warnings - string(REGEX MATCHALL - "\nPackage natbib Warning:[^\n]*" - natbib_warnings - "${log}") - if(natbib_warnings) - set(found_error TRUE) - message("\nFound natbib package warnings.") - foreach(warning ${natbib_warnings}) - string(STRIP "${warning}" warning_no_newline) - message("${warning_no_newline}") - endforeach(warning) - endif() - - # Check for overfull - string(REGEX MATCHALL - "\nOverfull[^\n]*" - overfull_warnings - "${log}") - if(overfull_warnings) - set(found_error TRUE) - message("\nFound overfull warnings. These are indicative of layout errors.") - foreach(warning ${overfull_warnings}) - string(STRIP "${warning}" warning_no_newline) - message("${warning_no_newline}") - endforeach(warning) - endif() - - # Check for invalid characters - string(REGEX MATCHALL - "\nMissing character:[^\n]*" - invalid_character_warnings - "${log}") - if(invalid_character_warnings) - set(found_error TRUE) - message("\nFound invalid character warnings. These characters are likely not printed correctly.") - foreach(warning ${invalid_character_warnings}) - string(STRIP "${warning}" warning_no_newline) - message("${warning_no_newline}") - endforeach(warning) - endif() - - if(found_error) - latex_get_filename_component(log_file_path ${log_file} ABSOLUTE) - message("\nConsult ${log_file_path} for more information on LaTeX build.") - else() - message("No known important warnings found.") - endif(found_error) -endfunction(latex_check_important_warnings) - -############################################################################# -# Helper functions for establishing LaTeX build. -############################################################################# - -function(latex_needit VAR NAME) - if(NOT ${VAR}) - message(SEND_ERROR "I need the ${NAME} command.") - endif() -endfunction(latex_needit) - -function(latex_wantit VAR NAME) - if(NOT ${VAR}) - message(STATUS "I could not find the ${NAME} command.") - endif() -endfunction(latex_wantit) - -function(latex_setup_variables) - set(LATEX_OUTPUT_PATH "${LATEX_OUTPUT_PATH}" - CACHE PATH "If non empty, specifies the location to place LaTeX output." - ) - - find_package(LATEX) - - find_program(XINDY_COMPILER - NAME xindy - PATHS ${MIKTEX_BINARY_PATH} /usr/bin - ) - - find_package(UnixCommands) - - find_program(PDFTOPS_CONVERTER - NAMES pdftops - DOC "The pdf to ps converter program from the Poppler package." - ) - - find_program(HTLATEX_COMPILER - NAMES htlatex - PATHS ${MIKTEX_BINARY_PATH} - /usr/bin - ) - - mark_as_advanced( - LATEX_COMPILER - PDFLATEX_COMPILER - BIBTEX_COMPILER - BIBER_COMPILER - MAKEINDEX_COMPILER - XINDY_COMPILER - DVIPS_CONVERTER - PS2PDF_CONVERTER - PDFTOPS_CONVERTER - LATEX2HTML_CONVERTER - HTLATEX_COMPILER - ) - - latex_needit(LATEX_COMPILER latex) - latex_wantit(PDFLATEX_COMPILER pdflatex) - latex_wantit(HTLATEX_COMPILER htlatex) - latex_needit(BIBTEX_COMPILER bibtex) - latex_wantit(BIBER_COMPILER biber) - latex_needit(MAKEINDEX_COMPILER makeindex) - latex_wantit(DVIPS_CONVERTER dvips) - latex_wantit(PS2PDF_CONVERTER ps2pdf) - latex_wantit(PDFTOPS_CONVERTER pdftops) - - set(LATEX_COMPILER_FLAGS "-interaction=batchmode -file-line-error" - CACHE STRING "Flags passed to latex.") - set(PDFLATEX_COMPILER_FLAGS ${LATEX_COMPILER_FLAGS} - CACHE STRING "Flags passed to pdflatex.") - set(HTLATEX_COMPILER_TEX4HT_FLAGS "html" - CACHE STRING "Options for the tex4ht.sty and *.4ht style files.") - set(HTLATEX_COMPILER_TEX4HT_POSTPROCESSOR_FLAGS "" - CACHE STRING "Options for the text4ht postprocessor.") - set(HTLATEX_COMPILER_T4HT_POSTPROCESSOR_FLAGS "" - CACHE STRING "Options for the t4ht postprocessor.") - set(HTLATEX_COMPILER_LATEX_FLAGS ${LATEX_COMPILER_FLAGS} - CACHE STRING "Flags passed from htlatex to the LaTeX compiler.") - set(LATEX_SYNCTEX_FLAGS "-synctex=1" - CACHE STRING "latex/pdflatex flags used to create synctex file.") - set(BIBTEX_COMPILER_FLAGS "" - CACHE STRING "Flags passed to bibtex.") - set(BIBER_COMPILER_FLAGS "" - CACHE STRING "Flags passed to biber.") - set(MAKEINDEX_COMPILER_FLAGS "" - CACHE STRING "Flags passed to makeindex.") - set(MAKEGLOSSARIES_COMPILER_FLAGS "" - CACHE STRING "Flags passed to makeglossaries.") - set(MAKENOMENCLATURE_COMPILER_FLAGS "" - CACHE STRING "Flags passed to makenomenclature.") - set(DVIPS_CONVERTER_FLAGS "-Ppdf -G0 -t letter" - CACHE STRING "Flags passed to dvips.") - if(NOT WIN32) - set(PS2PDF_CONVERTER_FLAGS "-dMaxSubsetPct=100 -dCompatibilityLevel=1.3 -dSubsetFonts=true -dEmbedAllFonts=true -dAutoFilterColorImages=false -dAutoFilterGrayImages=false -dColorImageFilter=/FlateEncode -dGrayImageFilter=/FlateEncode -dMonoImageFilter=/FlateEncode" - CACHE STRING "Flags passed to ps2pdf.") - else() - # Most windows ports of ghostscript utilities use .bat files for ps2pdf - # commands. bat scripts interpret "=" as a special character and separate - # those arguments. To get around this, the ghostscript utilities also - # support using "#" in place of "=". - set(PS2PDF_CONVERTER_FLAGS "-dMaxSubsetPct#100 -dCompatibilityLevel#1.3 -dSubsetFonts#true -dEmbedAllFonts#true -dAutoFilterColorImages#false -dAutoFilterGrayImages#false -dColorImageFilter#/FlateEncode -dGrayImageFilter#/FlateEncode -dMonoImageFilter#/FlateEncode" - CACHE STRING "Flags passed to ps2pdf.") - endif() - set(PDFTOPS_CONVERTER_FLAGS "" - CACHE STRING "Flags passed to pdftops.") - mark_as_advanced( - LATEX_COMPILER_FLAGS - PDFLATEX_COMPILER_FLAGS - HTLATEX_COMPILER_TEX4HT_FLAGS - HTLATEX_COMPILER_TEX4HT_POSTPROCESSOR_FLAGS - HTLATEX_COMPILER_T4HT_POSTPROCESSOR_FLAGS - HTLATEX_COMPILER_LATEX_FLAGS - LATEX_SYNCTEX_FLAGS - BIBTEX_COMPILER_FLAGS - BIBER_COMPILER_FLAGS - MAKEINDEX_COMPILER_FLAGS - MAKEGLOSSARIES_COMPILER_FLAGS - MAKENOMENCLATURE_COMPILER_FLAGS - DVIPS_CONVERTER_FLAGS - PS2PDF_CONVERTER_FLAGS - PDFTOPS_CONVERTER_FLAGS - ) - - # Because it is easier to type, the flags variables are entered as - # space-separated strings much like you would in a shell. However, when - # using a CMake command to execute a program, it works better to hold the - # arguments in semicolon-separated lists (otherwise the whole string will - # be interpreted as a single argument). Use the separate_arguments to - # convert the space-separated strings to semicolon-separated lists. - separate_arguments(LATEX_COMPILER_FLAGS) - separate_arguments(PDFLATEX_COMPILER_FLAGS) - separate_arguments(HTLATEX_COMPILER_LATEX_FLAGS) - separate_arguments(LATEX_SYNCTEX_FLAGS) - separate_arguments(BIBTEX_COMPILER_FLAGS) - separate_arguments(BIBER_COMPILER_FLAGS) - separate_arguments(MAKEINDEX_COMPILER_FLAGS) - separate_arguments(MAKEGLOSSARIES_COMPILER_FLAGS) - separate_arguments(MAKENOMENCLATURE_COMPILER_FLAGS) - separate_arguments(DVIPS_CONVERTER_FLAGS) - separate_arguments(PS2PDF_CONVERTER_FLAGS) - separate_arguments(PDFTOPS_CONVERTER_FLAGS) - - # Not quite done. When you call separate_arguments on a cache variable, - # the result is written to a local variable. That local variable goes - # away when this function returns (which is before any of them are used). - # So, copy these variables with local scope to cache variables with - # global scope. - set(LATEX_COMPILER_ARGS "${LATEX_COMPILER_FLAGS}" CACHE INTERNAL "") - set(PDFLATEX_COMPILER_ARGS "${PDFLATEX_COMPILER_FLAGS}" CACHE INTERNAL "") - set(HTLATEX_COMPILER_ARGS "${HTLATEX_COMPILER_LATEX_FLAGS}" CACHE INTERNAL "") - set(LATEX_SYNCTEX_ARGS "${LATEX_SYNCTEX_FLAGS}" CACHE INTERNAL "") - set(BIBTEX_COMPILER_ARGS "${BIBTEX_COMPILER_FLAGS}" CACHE INTERNAL "") - set(BIBER_COMPILER_ARGS "${BIBER_COMPILER_FLAGS}" CACHE INTERNAL "") - set(MAKEINDEX_COMPILER_ARGS "${MAKEINDEX_COMPILER_FLAGS}" CACHE INTERNAL "") - set(MAKEGLOSSARIES_COMPILER_ARGS "${MAKEGLOSSARIES_COMPILER_FLAGS}" CACHE INTERNAL "") - set(MAKENOMENCLATURE_COMPILER_ARGS "${MAKENOMENCLATURE_COMPILER_FLAGS}" CACHE INTERNAL "") - set(DVIPS_CONVERTER_ARGS "${DVIPS_CONVERTER_FLAGS}" CACHE INTERNAL "") - set(PS2PDF_CONVERTER_ARGS "${PS2PDF_CONVERTER_FLAGS}" CACHE INTERNAL "") - set(PDFTOPS_CONVERTER_ARGS "${PDFTOPS_CONVERTER_FLAGS}" CACHE INTERNAL "") - - find_program(IMAGEMAGICK_CONVERT - NAMES magick convert - DOC "The convert program that comes with ImageMagick (available at http://www.imagemagick.org)." - ) - mark_as_advanced(IMAGEMAGICK_CONVERT) - - if(DEFINED ENV{LATEX_DEFAULT_BUILD}) - set(default_build $ENV{LATEX_DEFAULT_BUILD}) - else() - set(default_build pdf) - endif() - - set(LATEX_DEFAULT_BUILD "${default_build}" CACHE STRING - "Choose the default type of LaTeX build. Valid options are pdf, dvi, ps, safepdf, html" - ) - set_property(CACHE LATEX_DEFAULT_BUILD - PROPERTY STRINGS pdf dvi ps safepdf html - ) - - option(LATEX_USE_SYNCTEX - "If on, have LaTeX generate a synctex file, which WYSIWYG editors can use to correlate output files like dvi and pdf with the lines of LaTeX source that generates them. In addition to adding the LATEX_SYNCTEX_FLAGS to the command line, this option also adds build commands that \"corrects\" the resulting synctex file to point to the original LaTeX files rather than those generated by UseLATEX.cmake." - OFF - ) - - option(LATEX_SMALL_IMAGES - "If on, the raster images will be converted to 1/6 the original size. This is because papers usually require 600 dpi images whereas most monitors only require at most 96 dpi. Thus, smaller images make smaller files for web distribution and can make it faster to read dvi files." - OFF) - if(LATEX_SMALL_IMAGES) - set(LATEX_RASTER_SCALE 16 PARENT_SCOPE) - set(LATEX_OPPOSITE_RASTER_SCALE 100 PARENT_SCOPE) - else() - set(LATEX_RASTER_SCALE 100 PARENT_SCOPE) - set(LATEX_OPPOSITE_RASTER_SCALE 16 PARENT_SCOPE) - endif() - - # Just holds extensions for known image types. They should all be lower case. - # For historical reasons, these are all declared in the global scope. - set(LATEX_DVI_VECTOR_IMAGE_EXTENSIONS .eps CACHE INTERNAL "") - set(LATEX_DVI_RASTER_IMAGE_EXTENSIONS CACHE INTERNAL "") - set(LATEX_DVI_IMAGE_EXTENSIONS - ${LATEX_DVI_VECTOR_IMAGE_EXTENSIONS} - ${LATEX_DVI_RASTER_IMAGE_EXTENSIONS} - CACHE INTERNAL "" - ) - - set(LATEX_PDF_VECTOR_IMAGE_EXTENSIONS .pdf CACHE INTERNAL "") - set(LATEX_PDF_RASTER_IMAGE_EXTENSIONS .jpeg .jpg .png CACHE INTERNAL "") - set(LATEX_PDF_IMAGE_EXTENSIONS - ${LATEX_PDF_VECTOR_IMAGE_EXTENSIONS} - ${LATEX_PDF_RASTER_IMAGE_EXTENSIONS} - CACHE INTERNAL "" - ) - - set(LATEX_OTHER_VECTOR_IMAGE_EXTENSIONS .ai .dot .svg CACHE INTERNAL "") - set(LATEX_OTHER_RASTER_IMAGE_EXTENSIONS - .bmp .bmp2 .bmp3 .dcm .dcx .ico .gif .pict .ppm .tif .tiff - CACHE INTERNAL "") - set(LATEX_OTHER_IMAGE_EXTENSIONS - ${LATEX_OTHER_VECTOR_IMAGE_EXTENSIONS} - ${LATEX_OTHER_RASTER_IMAGE_EXTENSIONS} - CACHE INTERNAL "" - ) - - set(LATEX_VECTOR_IMAGE_EXTENSIONS - ${LATEX_DVI_VECTOR_IMAGE_EXTENSIONS} - ${LATEX_PDF_VECTOR_IMAGE_EXTENSIONS} - ${LATEX_OTHER_VECTOR_IMAGE_EXTENSIONS} - CACHE INTERNAL "" - ) - set(LATEX_RASTER_IMAGE_EXTENSIONS - ${LATEX_DVI_RASTER_IMAGE_EXTENSIONS} - ${LATEX_PDF_RASTER_IMAGE_EXTENSIONS} - ${LATEX_OTHER_RASTER_IMAGE_EXTENSIONS} - CACHE INTERNAL "" - ) - set(LATEX_IMAGE_EXTENSIONS - ${LATEX_DVI_IMAGE_EXTENSIONS} - ${LATEX_PDF_IMAGE_EXTENSIONS} - ${LATEX_OTHER_IMAGE_EXTENSIONS} - CACHE INTERNAL "" - ) -endfunction(latex_setup_variables) - -function(latex_setup_targets) - if(NOT TARGET pdf) - add_custom_target(pdf) - endif() - if(NOT TARGET dvi) - add_custom_target(dvi) - endif() - if(NOT TARGET ps) - add_custom_target(ps) - endif() - if(NOT TARGET safepdf) - add_custom_target(safepdf) - endif() - if(NOT TARGET html) - add_custom_target(html) - endif() - if(NOT TARGET auxclean) - add_custom_target(auxclean) - endif() -endfunction(latex_setup_targets) - -function(latex_get_output_path var) - set(latex_output_path) - if(LATEX_OUTPUT_PATH) - get_filename_component( - LATEX_OUTPUT_PATH_FULL "${LATEX_OUTPUT_PATH}" ABSOLUTE - ) - if("${LATEX_OUTPUT_PATH_FULL}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") - message(SEND_ERROR "You cannot set LATEX_OUTPUT_PATH to the same directory that contains LaTeX input files.") - else() - set(latex_output_path "${LATEX_OUTPUT_PATH_FULL}") - endif() - else() - if("${CMAKE_CURRENT_BINARY_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") - message(SEND_ERROR "LaTeX files must be built out of source or you must set LATEX_OUTPUT_PATH.") - else() - set(latex_output_path "${CMAKE_CURRENT_BINARY_DIR}") - endif() - endif() - set(${var} ${latex_output_path} PARENT_SCOPE) -endfunction(latex_get_output_path) - -function(latex_add_convert_command - output_path - input_path - output_extension - input_extension - flags - ) - set(require_imagemagick_convert TRUE) - set(convert_flags "") - if(${input_extension} STREQUAL ".eps" AND ${output_extension} STREQUAL ".pdf") - # ImageMagick has broken eps to pdf conversion - # use ps2pdf instead - if(PS2PDF_CONVERTER) - set(require_imagemagick_convert FALSE) - set(converter ${PS2PDF_CONVERTER}) - set(convert_flags -dEPSCrop ${PS2PDF_CONVERTER_ARGS}) - else() - message(SEND_ERROR "Using postscript files with pdflatex requires ps2pdf for conversion.") - endif() - elseif(${input_extension} STREQUAL ".pdf" AND ${output_extension} STREQUAL ".eps") - # ImageMagick can also be sketchy on pdf to eps conversion. Not good with - # color spaces and tends to unnecessarily rasterize. - # use pdftops instead - if(PDFTOPS_CONVERTER) - set(require_imagemagick_convert FALSE) - set(converter ${PDFTOPS_CONVERTER}) - set(convert_flags -eps ${PDFTOPS_CONVERTER_ARGS}) - else() - message(STATUS "Consider getting pdftops from Poppler to convert PDF images to EPS images.") - set(convert_flags ${flags}) - endif() - else() - set(convert_flags ${flags}) - endif() - - if(require_imagemagick_convert) - if(IMAGEMAGICK_CONVERT) - string(TOLOWER ${IMAGEMAGICK_CONVERT} IMAGEMAGICK_CONVERT_LOWERCASE) - if(${IMAGEMAGICK_CONVERT_LOWERCASE} MATCHES "system32[/\\\\]convert\\.exe") - message(SEND_ERROR "IMAGEMAGICK_CONVERT set to Window's convert.exe for changing file systems rather than ImageMagick's convert for changing image formats. Please make sure ImageMagick is installed (available at http://www.imagemagick.org). If you have a recent version of ImageMagick (7.0 or higher), use the magick program instead of convert for IMAGEMAGICK_CONVERT.") - else() - set(converter ${IMAGEMAGICK_CONVERT}) - # ImageMagick requires a special order of arguments where resize and - # arguments of that nature must be placed after the input image path. - add_custom_command(OUTPUT ${output_path} - COMMAND ${converter} - ARGS ${input_path} ${convert_flags} ${output_path} - DEPENDS ${input_path} - ) - endif() - else() - message(SEND_ERROR "Could not find convert program. Please download ImageMagick from http://www.imagemagick.org and install.") - endif() - else() # Not ImageMagick convert - add_custom_command(OUTPUT ${output_path} - COMMAND ${converter} - ARGS ${convert_flags} ${input_path} ${output_path} - DEPENDS ${input_path} - ) - endif() -endfunction(latex_add_convert_command) - -# Makes custom commands to convert a file to a particular type. -function(latex_convert_image - output_files_var - input_file - output_extension - convert_flags - output_extensions - other_files - ) - set(output_file_list) - set(input_dir ${CMAKE_CURRENT_SOURCE_DIR}) - latex_get_output_path(output_dir) - - latex_get_filename_component(extension "${input_file}" EXT) - - # Check input filename for potential problems with LaTeX. - latex_get_filename_component(name "${input_file}" NAME_WE) - set(suggested_name "${name}") - if(suggested_name MATCHES ".*\\..*") - string(REPLACE "." "-" suggested_name "${suggested_name}") - endif() - if(suggested_name MATCHES ".* .*") - string(REPLACE " " "-" suggested_name "${suggested_name}") - endif() - if(NOT suggested_name STREQUAL name) - message(WARNING "Some LaTeX distributions have problems with image file names with multiple extensions or spaces. Consider changing ${name}${extension} to something like ${suggested_name}${extension}.") - endif() - - string(REGEX REPLACE "\\.[^.]*\$" ${output_extension} output_file - "${input_file}") - - latex_list_contains(is_type ${extension} ${output_extensions}) - if(is_type) - if(convert_flags) - latex_add_convert_command(${output_dir}/${output_file} - ${input_dir}/${input_file} ${output_extension} ${extension} - "${convert_flags}") - set(output_file_list ${output_dir}/${output_file}) - else() - # As a shortcut, we can just copy the file. - add_custom_command(OUTPUT ${output_dir}/${input_file} - COMMAND ${CMAKE_COMMAND} - ARGS -E copy ${input_dir}/${input_file} ${output_dir}/${input_file} - DEPENDS ${input_dir}/${input_file} - ) - set(output_file_list ${output_dir}/${input_file}) - endif() - else() - set(do_convert TRUE) - # Check to see if there is another input file of the appropriate type. - foreach(valid_extension ${output_extensions}) - string(REGEX REPLACE "\\.[^.]*\$" ${output_extension} try_file - "${input_file}") - latex_list_contains(has_native_file "${try_file}" ${other_files}) - if(has_native_file) - set(do_convert FALSE) - endif() - endforeach(valid_extension) - - # If we still need to convert, do it. - if(do_convert) - latex_add_convert_command(${output_dir}/${output_file} - ${input_dir}/${input_file} ${output_extension} ${extension} - "${convert_flags}") - set(output_file_list ${output_dir}/${output_file}) - endif() - endif() - - set(${output_files_var} ${output_file_list} PARENT_SCOPE) -endfunction(latex_convert_image) - -# Adds custom commands to process the given files for dvi and pdf builds. -# Adds the output files to the given variables (does not replace). -function(latex_process_images dvi_outputs_var pdf_outputs_var) - latex_get_output_path(output_dir) - set(dvi_outputs) - set(pdf_outputs) - foreach(file ${ARGN}) - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${file}") - latex_get_filename_component(extension "${file}" EXT) - set(convert_flags) - - # Check to see if we need to downsample the image. - latex_list_contains(is_raster "${extension}" - ${LATEX_RASTER_IMAGE_EXTENSIONS}) - if(LATEX_SMALL_IMAGES) - if(is_raster) - set(convert_flags -resize ${LATEX_RASTER_SCALE}%) - endif() - endif() - - # Make sure the output directory exists. - latex_get_filename_component(path "${output_dir}/${file}" PATH) - make_directory("${path}") - - # Do conversions for dvi. - if(NOT LATEX_FORCE_PDF) - latex_convert_image(output_files "${file}" .eps "${convert_flags}" - "${LATEX_DVI_IMAGE_EXTENSIONS}" "${ARGN}") - list(APPEND dvi_outputs ${output_files}) - endif () - - # Do conversions for pdf. - if(NOT LATEX_FORCE_DVI AND NOT LATEX_FORCE_HTML) - if(is_raster) - latex_convert_image(output_files "${file}" .png "${convert_flags}" - "${LATEX_PDF_IMAGE_EXTENSIONS}" "${ARGN}") - list(APPEND pdf_outputs ${output_files}) - else() - latex_convert_image(output_files "${file}" .pdf "${convert_flags}" - "${LATEX_PDF_IMAGE_EXTENSIONS}" "${ARGN}") - list(APPEND pdf_outputs ${output_files}) - endif() - endif() - else() - message(WARNING "Could not find file ${CMAKE_CURRENT_SOURCE_DIR}/${file}. Are you sure you gave relative paths to IMAGES?") - endif() - endforeach(file) - - set(${dvi_outputs_var} ${dvi_outputs} PARENT_SCOPE) - set(${pdf_outputs_var} ${pdf_outputs} PARENT_SCOPE) -endfunction(latex_process_images) - -function(latex_copy_globbed_files pattern dest) - if(${CMAKE_VERSION} VERSION_LESS "3.12") - file(GLOB file_list ${pattern}) - else() - file(GLOB file_list CONFIGURE_DEPENDS ${pattern}) - endif() - foreach(in_file ${file_list}) - latex_get_filename_component(out_file ${in_file} NAME) - configure_file(${in_file} ${dest}/${out_file} COPYONLY) - endforeach(in_file) -endfunction(latex_copy_globbed_files) - -function(latex_copy_input_file file) - latex_get_output_path(output_dir) - - if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}) - latex_get_filename_component(path ${file} PATH) - file(MAKE_DIRECTORY ${output_dir}/${path}) - - latex_list_contains(use_config ${file} ${LATEX_CONFIGURE}) - if(use_config) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${file} - ${output_dir}/${file} - @ONLY - ) - add_custom_command(OUTPUT ${output_dir}/${file} - COMMAND ${CMAKE_COMMAND} - ARGS ${CMAKE_BINARY_DIR} - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${file} - ) - else() - add_custom_command(OUTPUT ${output_dir}/${file} - COMMAND ${CMAKE_COMMAND} - ARGS -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${file} ${output_dir}/${file} - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${file} - ) - endif() - else() - if(EXISTS ${output_dir}/${file}) - # Special case: output exists but input does not. Assume that it was - # created elsewhere and skip the input file copy. - else() - message("Could not find input file ${CMAKE_CURRENT_SOURCE_DIR}/${file}") - endif() - endif() -endfunction(latex_copy_input_file) - -############################################################################# -# Commands provided by the UseLATEX.cmake "package" -############################################################################# - -function(latex_usage command message) - message(SEND_ERROR - "${message}\n Usage: ${command}(\n [BIBFILES ...]\n [INPUTS ...]\n [IMAGE_DIRS ...]\n [IMAGES \n [CONFIGURE ...]\n [DEPENDS ...]\n [MULTIBIB_NEWCITES] \n [USE_BIBLATEX] [USE_INDEX] [USE_GLOSSARY] [USE_NOMENCL]\n [FORCE_PDF] [FORCE_DVI] [FORCE_HTML]\n [TARGET_NAME] \n [EXCLUDE_FROM_ALL]\n [EXCLUDE_FROM_DEFAULTS])" - ) -endfunction(latex_usage command message) - -# Parses arguments to add_latex_document and ADD_LATEX_TARGETS and sets the -# variables LATEX_TARGET, LATEX_IMAGE_DIR, LATEX_BIBFILES, LATEX_DEPENDS, and -# LATEX_INPUTS. -function(parse_add_latex_arguments command latex_main_input) - set(options - USE_BIBLATEX - USE_INDEX - USE_GLOSSARY - USE_NOMENCL - FORCE_PDF - FORCE_DVI - FORCE_HTML - EXCLUDE_FROM_ALL - EXCLUDE_FROM_DEFAULTS - # Deprecated options - USE_GLOSSARIES - DEFAULT_PDF - DEFAULT_SAFEPDF - DEFAULT_PS - NO_DEFAULT - MANGLE_TARGET_NAMES - ) - set(oneValueArgs - TARGET_NAME - ) - set(multiValueArgs - BIBFILES - MULTIBIB_NEWCITES - INPUTS - IMAGE_DIRS - IMAGES - CONFIGURE - DEPENDS - INDEX_NAMES - INCLUDE_DIRECTORIES - ) - cmake_parse_arguments( - LATEX "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Handle invalid and deprecated arguments - if(LATEX_UNPARSED_ARGUMENTS) - latex_usage(${command} "Invalid or deprecated arguments: ${LATEX_UNPARSED_ARGUMENTS}") - endif() - if(LATEX_USE_GLOSSARIES) - latex_usage(${command} "USE_GLOSSARIES option removed in version 1.6.1. Use USE_GLOSSARY instead.") - endif() - if(LATEX_DEFAULT_PDF) - latex_usage(${command} "DEFAULT_PDF option removed in version 2.0. Use FORCE_PDF option or LATEX_DEFAULT_BUILD CMake variable instead.") - endif() - if(LATEX_DEFAULT_SAFEPDF) - latex_usage(${command} "DEFAULT_SAFEPDF option removed in version 2.0. Use LATEX_DEFAULT_BUILD CMake variable instead.") - endif() - if(LATEX_DEFAULT_DVI) - latex_usage(${command} "DEFAULT_DVI option removed in version 2.0. Use FORCE_DVI option or LATEX_DEFAULT_BUILD CMake variable instead.") - endif() - if(LATEX_NO_DEFAULT) - latex_usage(${command} "NO_DEFAULT option removed in version 2.0. Use EXCLUDE_FROM_ALL instead.") - endif() - if(LATEX_MANGLE_TARGET_NAMES) - latex_usage(${command} "MANGLE_TARGET_NAMES option removed in version 2.0. All LaTeX targets use mangled names now.") - endif() - - # Capture the first argument, which is the main LaTeX input. - latex_get_filename_component(latex_target ${latex_main_input} NAME_WE) - set(LATEX_MAIN_INPUT ${latex_main_input} PARENT_SCOPE) - set(LATEX_TARGET ${latex_target} PARENT_SCOPE) - - # Propagate the result variables to the caller - foreach(arg_name ${options} ${oneValueArgs} ${multiValueArgs}) - set(var_name LATEX_${arg_name}) - set(${var_name} ${${var_name}} PARENT_SCOPE) - endforeach(arg_name) -endfunction(parse_add_latex_arguments) - -function(add_latex_targets_internal) - latex_get_output_path(output_dir) - - if(LATEX_USE_SYNCTEX) - set(synctex_flags ${LATEX_SYNCTEX_ARGS}) - else() - set(synctex_flags) - endif() - - # The commands to run LaTeX. They are repeated multiple times. - set(latex_build_command - ${LATEX_COMPILER} ${LATEX_COMPILER_ARGS} ${synctex_flags} ${LATEX_MAIN_INPUT} - ) - if(LATEX_COMPILER_ARGS MATCHES ".*batchmode.*") - # Wrap command in script that dumps the log file on error. This makes sure - # errors can be seen. - set(latex_build_command - ${CMAKE_COMMAND} - -D LATEX_BUILD_COMMAND=execute_latex - -D LATEX_WORKING_DIRECTORY="${output_dir}" - -D LATEX_FULL_COMMAND="${latex_build_command}" - -D LATEX_OUTPUT_FILE="${LATEX_TARGET}.dvi" - -D LATEX_LOG_FILE="${LATEX_TARGET}.log" - -P "${LATEX_USE_LATEX_LOCATION}" - ) - endif() - set(pdflatex_build_command - ${PDFLATEX_COMPILER} ${PDFLATEX_COMPILER_ARGS} ${synctex_flags} ${LATEX_MAIN_INPUT} - ) - if(PDFLATEX_COMPILER_ARGS MATCHES ".*batchmode.*") - # Wrap command in script that dumps the log file on error. This makes sure - # errors can be seen. - set(pdflatex_build_command - ${CMAKE_COMMAND} - -D LATEX_BUILD_COMMAND=execute_latex - -D LATEX_WORKING_DIRECTORY="${output_dir}" - -D LATEX_FULL_COMMAND="${pdflatex_build_command}" - -D LATEX_OUTPUT_FILE="${LATEX_TARGET}.pdf" - -D LATEX_LOG_FILE="${LATEX_TARGET}.log" - -P "${LATEX_USE_LATEX_LOCATION}" - ) - endif() - - if(LATEX_INCLUDE_DIRECTORIES) - # The include directories needs to start with the build directory so - # that the copied files can be found. It also needs to end with an - # empty directory so that the standard system directories are included - # after any specified. - set(LATEX_INCLUDE_DIRECTORIES . ${LATEX_INCLUDE_DIRECTORIES} "") - - # CMake separates items in a list with a semicolon. Lists of - # directories on most systems are separated by colons, so we can do a - # simple text replace. On Windows, directories are separated by - # semicolons, but we replace them with the $ generator - # expression to make sure CMake treats it as a single string. - if(CMAKE_HOST_WIN32) - string(REPLACE ";" "$" TEXINPUTS "${LATEX_INCLUDE_DIRECTORIES}") - else() - string(REPLACE ";" ":" TEXINPUTS "${LATEX_INCLUDE_DIRECTORIES}") - endif() - - # Set the TEXINPUTS environment variable - set(latex_build_command - ${CMAKE_COMMAND} -E env TEXINPUTS=${TEXINPUTS} ${latex_build_command}) - set(pdflatex_build_command - ${CMAKE_COMMAND} -E env TEXINPUTS=${TEXINPUTS} ${pdflatex_build_command}) - endif() - - if(NOT LATEX_TARGET_NAME) - # Use the main filename (minus the .tex) as the target name. Remove any - # spaces since CMake cannot have spaces in its target names. - string(REPLACE " " "_" LATEX_TARGET_NAME ${LATEX_TARGET}) - endif() - - # Some LaTeX commands may need to be modified (or may not work) if the main - # tex file is in a subdirectory. Make a flag for that. - get_filename_component(LATEX_MAIN_INPUT_SUBDIR ${LATEX_MAIN_INPUT} DIRECTORY) - - # Set up target names. - set(dvi_target ${LATEX_TARGET_NAME}_dvi) - set(pdf_target ${LATEX_TARGET_NAME}_pdf) - set(ps_target ${LATEX_TARGET_NAME}_ps) - set(safepdf_target ${LATEX_TARGET_NAME}_safepdf) - set(html_target ${LATEX_TARGET_NAME}_html) - set(auxclean_target ${LATEX_TARGET_NAME}_auxclean) - - # Probably not all of these will be generated, but they could be. - # Note that the aux file is added later. - set(auxiliary_clean_files - ${output_dir}/${LATEX_TARGET}.aux - ${output_dir}/${LATEX_TARGET}.bbl - ${output_dir}/${LATEX_TARGET}.blg - ${output_dir}/${LATEX_TARGET}-blx.bib - ${output_dir}/${LATEX_TARGET}.glg - ${output_dir}/${LATEX_TARGET}.glo - ${output_dir}/${LATEX_TARGET}.gls - ${output_dir}/${LATEX_TARGET}.idx - ${output_dir}/${LATEX_TARGET}.ilg - ${output_dir}/${LATEX_TARGET}.ind - ${output_dir}/${LATEX_TARGET}.ist - ${output_dir}/${LATEX_TARGET}.log - ${output_dir}/${LATEX_TARGET}.out - ${output_dir}/${LATEX_TARGET}.toc - ${output_dir}/${LATEX_TARGET}.lof - ${output_dir}/${LATEX_TARGET}.xdy - ${output_dir}/${LATEX_TARGET}.synctex.gz - ${output_dir}/${LATEX_TARGET}.synctex.bak.gz - ${output_dir}/${LATEX_TARGET}.dvi - ${output_dir}/${LATEX_TARGET}.ps - ${output_dir}/${LATEX_TARGET}.pdf - ) - - set(image_list ${LATEX_IMAGES}) - - # For each directory in LATEX_IMAGE_DIRS, glob all the image files and - # place them in LATEX_IMAGES. - foreach(dir ${LATEX_IMAGE_DIRS}) - if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${dir}) - message(WARNING "Image directory ${CMAKE_CURRENT_SOURCE_DIR}/${dir} does not exist. Are you sure you gave relative directories to IMAGE_DIRS?") - endif() - foreach(extension ${LATEX_IMAGE_EXTENSIONS}) - if(${CMAKE_VERSION} VERSION_LESS "3.12") - file(GLOB files ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*${extension}) - else() - file(GLOB files CONFIGURE_DEPENDS - ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*${extension} - ) - endif() - foreach(file ${files}) - latex_get_filename_component(filename ${file} NAME) - list(APPEND image_list ${dir}/${filename}) - endforeach(file) - endforeach(extension) - endforeach(dir) - - latex_process_images(dvi_images pdf_images ${image_list}) - - set(make_dvi_command - ${CMAKE_COMMAND} -E chdir ${output_dir} - ${latex_build_command}) - set(make_pdf_command - ${CMAKE_COMMAND} -E chdir ${output_dir} - ${pdflatex_build_command} - ) - - set(make_dvi_depends ${LATEX_DEPENDS} ${dvi_images}) - set(make_pdf_depends ${LATEX_DEPENDS} ${pdf_images}) - foreach(input ${LATEX_MAIN_INPUT} ${LATEX_INPUTS}) - list(APPEND make_dvi_depends ${output_dir}/${input}) - list(APPEND make_pdf_depends ${output_dir}/${input}) - if(${input} MATCHES "\\.tex$") - # Dependent .tex files might have their own .aux files created. Make - # sure these get cleaned as well. This might replicate the cleaning - # of the main .aux file, which is OK. - string(REGEX REPLACE "\\.tex$" "" input_we ${input}) - list(APPEND auxiliary_clean_files - ${output_dir}/${input_we}.aux - ${output_dir}/${input}.aux - ) - endif() - endforeach(input) - - set(all_latex_sources ${LATEX_MAIN_INPUT} ${LATEX_INPUTS} ${image_list}) - - if(LATEX_USE_GLOSSARY) - foreach(dummy 0 1) # Repeat these commands twice. - set(make_dvi_command ${make_dvi_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${CMAKE_COMMAND} - -D LATEX_BUILD_COMMAND=makeglossaries - -D LATEX_TARGET=${LATEX_TARGET} - -D MAKEINDEX_COMPILER=${MAKEINDEX_COMPILER} - -D XINDY_COMPILER=${XINDY_COMPILER} - -D MAKEGLOSSARIES_COMPILER_ARGS=${MAKEGLOSSARIES_COMPILER_ARGS} - -P ${LATEX_USE_LATEX_LOCATION} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${latex_build_command} - ) - set(make_pdf_command ${make_pdf_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${CMAKE_COMMAND} - -D LATEX_BUILD_COMMAND=makeglossaries - -D LATEX_TARGET=${LATEX_TARGET} - -D MAKEINDEX_COMPILER=${MAKEINDEX_COMPILER} - -D XINDY_COMPILER=${XINDY_COMPILER} - -D MAKEGLOSSARIES_COMPILER_ARGS=${MAKEGLOSSARIES_COMPILER_ARGS} - -P ${LATEX_USE_LATEX_LOCATION} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${pdflatex_build_command} - ) - endforeach(dummy) - endif() - - if(LATEX_USE_NOMENCL) - foreach(dummy 0 1) # Repeat these commands twice. - set(make_dvi_command ${make_dvi_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${CMAKE_COMMAND} - -D LATEX_BUILD_COMMAND=makenomenclature - -D LATEX_TARGET=${LATEX_TARGET} - -D MAKEINDEX_COMPILER=${MAKEINDEX_COMPILER} - -D MAKENOMENCLATURE_COMPILER_ARGS=${MAKENOMENCLATURE_COMPILER_ARGS} - -P ${LATEX_USE_LATEX_LOCATION} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${latex_build_command} - ) - set(make_pdf_command ${make_pdf_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${CMAKE_COMMAND} - -D LATEX_BUILD_COMMAND=makenomenclature - -D LATEX_TARGET=${LATEX_TARGET} - -D MAKEINDEX_COMPILER=${MAKEINDEX_COMPILER} - -D MAKENOMENCLATURE_COMPILER_ARGS=${MAKENOMENCLATURE_COMPILER_ARGS} - -P ${LATEX_USE_LATEX_LOCATION} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${pdflatex_build_command} - ) - endforeach(dummy) - endif() - - if(LATEX_BIBFILES) - set(suppress_bib_output) - if(LATEX_USE_BIBLATEX) - if(NOT BIBER_COMPILER) - message(SEND_ERROR "I need the biber command.") - endif() - set(bib_compiler ${BIBER_COMPILER}) - set(bib_compiler_flags ${BIBER_COMPILER_ARGS}) - - if(NOT BIBER_COMPILER_ARGS MATCHES ".*-q.*") - # Only suppress bib output if the quiet option is not specified. - set(suppress_bib_output TRUE) - endif() - - if(LATEX_USE_BIBLATEX_CONFIG) - list(APPEND auxiliary_clean_files ${output_dir}/biblatex.cfg) - list(APPEND make_dvi_depends ${output_dir}/biblatex.cfg) - list(APPEND make_pdf_depends ${output_dir}/biblatex.cfg) - endif() - else() - set(bib_compiler ${BIBTEX_COMPILER}) - set(bib_compiler_flags ${BIBTEX_COMPILER_ARGS}) - endif() - if(LATEX_MULTIBIB_NEWCITES) - # Suppressed bib output currently not supported for multibib - foreach (multibib_auxfile ${LATEX_MULTIBIB_NEWCITES}) - latex_get_filename_component(multibib_target ${multibib_auxfile} NAME_WE) - set(make_dvi_command ${make_dvi_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${bib_compiler} ${bib_compiler_flags} ${multibib_target}) - set(make_pdf_command ${make_pdf_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${bib_compiler} ${bib_compiler_flags} ${multibib_target}) - set(auxiliary_clean_files ${auxiliary_clean_files} - ${output_dir}/${multibib_target}.aux) - endforeach (multibib_auxfile ${LATEX_MULTIBIB_NEWCITES}) - else() - set(full_bib_command - ${bib_compiler} ${bib_compiler_flags} ${LATEX_TARGET}) - if(suppress_bib_output) - set(full_bib_command - ${CMAKE_COMMAND} - -D LATEX_BUILD_COMMAND=execute_latex - -D LATEX_WORKING_DIRECTORY="${output_dir}" - -D LATEX_FULL_COMMAND="${full_bib_command}" - -D LATEX_OUTPUT_FILE="${LATEX_TARGET}.bbl" - -D LATEX_LOG_FILE="${LATEX_TARGET}.blg" - -P "${LATEX_USE_LATEX_LOCATION}" - ) - endif() - set(make_dvi_command ${make_dvi_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${full_bib_command}) - set(make_pdf_command ${make_pdf_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${full_bib_command}) - endif() - - foreach (bibfile ${LATEX_BIBFILES}) - list(APPEND make_dvi_depends ${output_dir}/${bibfile}) - list(APPEND make_pdf_depends ${output_dir}/${bibfile}) - endforeach (bibfile ${LATEX_BIBFILES}) - else() - if(LATEX_MULTIBIB_NEWCITES) - message(WARNING "MULTIBIB_NEWCITES has no effect without BIBFILES option.") - endif() - endif() - - if(LATEX_USE_INDEX) - if(LATEX_INDEX_NAMES) - set(INDEX_NAMES ${LATEX_INDEX_NAMES}) - else() - set(INDEX_NAMES ${LATEX_TARGET}) - endif() - foreach(idx_name ${INDEX_NAMES}) - set(make_dvi_command ${make_dvi_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${latex_build_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${MAKEINDEX_COMPILER} ${MAKEINDEX_COMPILER_ARGS} ${idx_name}.idx) - set(make_pdf_command ${make_pdf_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${pdflatex_build_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${MAKEINDEX_COMPILER} ${MAKEINDEX_COMPILER_ARGS} ${idx_name}.idx) - set(auxiliary_clean_files ${auxiliary_clean_files} - ${output_dir}/${idx_name}.idx - ${output_dir}/${idx_name}.ilg - ${output_dir}/${idx_name}.ind) - endforeach() - else() - if(LATEX_INDEX_NAMES) - message(WARNING "INDEX_NAMES has no effect without USE_INDEX option.") - endif() - endif() - - set(make_dvi_command ${make_dvi_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${latex_build_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${latex_build_command}) - set(make_pdf_command ${make_pdf_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${pdflatex_build_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${pdflatex_build_command}) - - # Need to run one more time to remove biblatex' warning - # about page breaks that have changed. - if(LATEX_USE_BIBLATEX) - set(make_dvi_command ${make_dvi_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${latex_build_command}) - set(make_pdf_command ${make_pdf_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${pdflatex_build_command}) - endif() - - if(LATEX_USE_SYNCTEX) - if(NOT GZIP) - message(SEND_ERROR "UseLATEX.cmake: USE_SYNTEX option requires gzip program. Set GZIP variable.") - endif() - set(make_dvi_command ${make_dvi_command} - COMMAND ${CMAKE_COMMAND} - -D LATEX_BUILD_COMMAND=correct_synctex - -D LATEX_TARGET=${LATEX_TARGET} - -D GZIP=${GZIP} - -D "LATEX_SOURCE_DIRECTORY=${CMAKE_CURRENT_SOURCE_DIR}" - -D "LATEX_BINARY_DIRECTORY=${output_dir}" - -P ${LATEX_USE_LATEX_LOCATION} - ) - set(make_pdf_command ${make_pdf_command} - COMMAND ${CMAKE_COMMAND} - -D LATEX_BUILD_COMMAND=correct_synctex - -D LATEX_TARGET=${LATEX_TARGET} - -D GZIP=${GZIP} - -D "LATEX_SOURCE_DIRECTORY=${CMAKE_CURRENT_SOURCE_DIR}" - -D "LATEX_BINARY_DIRECTORY=${output_dir}" - -P ${LATEX_USE_LATEX_LOCATION} - ) - endif() - - # Check LaTeX output for important warnings at end of build - set(make_dvi_command ${make_dvi_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${CMAKE_COMMAND} - -D LATEX_BUILD_COMMAND=check_important_warnings - -D LATEX_TARGET=${LATEX_TARGET} - -P ${LATEX_USE_LATEX_LOCATION} - ) - set(make_pdf_command ${make_pdf_command} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${CMAKE_COMMAND} - -D LATEX_BUILD_COMMAND=check_important_warnings - -D LATEX_TARGET=${LATEX_TARGET} - -P ${LATEX_USE_LATEX_LOCATION} - ) - - # Capture the default build. - string(TOLOWER "${LATEX_DEFAULT_BUILD}" default_build) - - if((NOT LATEX_FORCE_PDF) AND (NOT LATEX_FORCE_DVI) AND (NOT LATEX_FORCE_HTML)) - set(no_force TRUE) - endif() - - # Add commands and targets for building pdf outputs (with pdflatex). - if(LATEX_FORCE_PDF OR no_force) - if(LATEX_FORCE_PDF) - set(default_build pdf) - endif() - - if(PDFLATEX_COMPILER) - add_custom_command(OUTPUT ${output_dir}/${LATEX_TARGET}.pdf - COMMAND ${make_pdf_command} - DEPENDS ${make_pdf_depends} - ) - add_custom_target(${pdf_target} - DEPENDS ${output_dir}/${LATEX_TARGET}.pdf - SOURCES ${all_latex_sources} - ) - if(NOT LATEX_EXCLUDE_FROM_DEFAULTS) - add_dependencies(pdf ${pdf_target}) - endif() - endif() - endif() - - # Add commands and targets for building dvi outputs. - if(LATEX_FORCE_DVI OR LATEX_FORCE_HTML OR no_force) - if(LATEX_FORCE_DVI) - if((NOT default_build STREQUAL dvi) AND - (NOT default_build STREQUAL ps) AND - (NOT default_build STREQUAL safepdf)) - set(default_build dvi) - endif() - endif() - - add_custom_command(OUTPUT ${output_dir}/${LATEX_TARGET}.dvi - COMMAND ${make_dvi_command} - DEPENDS ${make_dvi_depends} - ) - add_custom_target(${dvi_target} - DEPENDS ${output_dir}/${LATEX_TARGET}.dvi - SOURCES ${all_latex_sources} - ) - if(NOT LATEX_EXCLUDE_FROM_DEFAULTS) - add_dependencies(dvi ${dvi_target}) - endif() - - if(DVIPS_CONVERTER) - add_custom_command(OUTPUT ${output_dir}/${LATEX_TARGET}.ps - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${DVIPS_CONVERTER} ${DVIPS_CONVERTER_ARGS} -o ${LATEX_TARGET}.ps ${LATEX_TARGET}.dvi - DEPENDS ${output_dir}/${LATEX_TARGET}.dvi) - add_custom_target(${ps_target} - DEPENDS ${output_dir}/${LATEX_TARGET}.ps - SOURCES ${all_latex_sources} - ) - if(NOT LATEX_EXCLUDE_FROM_DEFAULTS) - add_dependencies(ps ${ps_target}) - endif() - if(PS2PDF_CONVERTER) - # Since both the pdf and safepdf targets have the same output, we - # cannot properly do the dependencies for both. When selecting safepdf, - # simply force a recompile every time. - add_custom_target(${safepdf_target} - ${CMAKE_COMMAND} -E chdir ${output_dir} - ${PS2PDF_CONVERTER} ${PS2PDF_CONVERTER_ARGS} ${LATEX_TARGET}.ps ${LATEX_TARGET}.pdf - DEPENDS ${ps_target} - ) - if(NOT LATEX_EXCLUDE_FROM_DEFAULTS) - add_dependencies(safepdf ${safepdf_target}) - endif() - endif() - endif() - endif() - - if(LATEX_FORCE_HTML OR no_force) - if (LATEX_FORCE_HTML) - set(default_build html) - endif() - - if(HTLATEX_COMPILER AND LATEX_MAIN_INPUT_SUBDIR) - message(STATUS - "Disabling HTML build for ${LATEX_TARGET_NAME}.tex because the main file is in subdirectory ${LATEX_MAIN_INPUT_SUBDIR}" - ) - # The code below to run HTML assumes that LATEX_TARGET.tex is in the - # current directory. I have tried to specify that LATEX_TARGET.tex is - # in a subdirectory. That makes the build targets correct, but the - # HTML build still fails (at least for htlatex) because files are not - # generated where expected. I am getting around the problem by simply - # disabling HTML in this case. If someone really cares, they can fix - # this, but make sure it runs on many platforms and build programs. - elseif(HTLATEX_COMPILER) - # htlatex places the output in a different location - set(HTML_OUTPUT "${output_dir}/${LATEX_TARGET}.html") - add_custom_command(OUTPUT ${HTML_OUTPUT} - COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} - ${HTLATEX_COMPILER} ${LATEX_MAIN_INPUT} - "${HTLATEX_COMPILER_TEX4HT_FLAGS}" - "${HTLATEX_COMPILER_TEX4HT_POSTPROCESSOR_FLAGS}" - "${HTLATEX_COMPILER_T4HT_POSTPROCESSOR_FLAGS}" - ${HTLATEX_COMPILER_ARGS} - DEPENDS - ${output_dir}/${LATEX_TARGET}.tex - ${output_dir}/${LATEX_TARGET}.dvi - VERBATIM - ) - add_custom_target(${html_target} - DEPENDS ${HTML_OUTPUT} ${dvi_target} - SOURCES ${all_latex_sources} - ) - if(NOT LATEX_EXCLUDE_FROM_DEFAULTS) - add_dependencies(html ${html_target}) - endif() - endif() - endif() - - # Set default targets. - if("${default_build}" STREQUAL "pdf") - add_custom_target(${LATEX_TARGET_NAME} DEPENDS ${pdf_target}) - elseif("${default_build}" STREQUAL "dvi") - add_custom_target(${LATEX_TARGET_NAME} DEPENDS ${dvi_target}) - elseif("${default_build}" STREQUAL "ps") - add_custom_target(${LATEX_TARGET_NAME} DEPENDS ${ps_target}) - elseif("${default_build}" STREQUAL "safepdf") - add_custom_target(${LATEX_TARGET_NAME} DEPENDS ${safepdf_target}) - elseif("${default_build}" STREQUAL "html") - add_custom_target(${LATEX_TARGET_NAME} DEPENDS ${html_target}) - else() - message(SEND_ERROR "LATEX_DEFAULT_BUILD set to an invalid value. See the documentation for that variable.") - endif() - - if(NOT LATEX_EXCLUDE_FROM_ALL) - add_custom_target(_${LATEX_TARGET_NAME} ALL DEPENDS ${LATEX_TARGET_NAME}) - endif() - - set_directory_properties(. - ADDITIONAL_MAKE_CLEAN_FILES "${auxiliary_clean_files}" - ) - - add_custom_target(${auxclean_target} - COMMENT "Cleaning auxiliary LaTeX files." - COMMAND ${CMAKE_COMMAND} -E remove ${auxiliary_clean_files} - ) - add_dependencies(auxclean ${auxclean_target}) -endfunction(add_latex_targets_internal) - -function(add_latex_targets latex_main_input) - latex_get_output_path(output_dir) - parse_add_latex_arguments(ADD_LATEX_TARGETS ${latex_main_input} ${ARGN}) - - add_latex_targets_internal() -endfunction(add_latex_targets) - -function(add_latex_document latex_main_input) - latex_get_output_path(output_dir) - if(output_dir) - parse_add_latex_arguments(add_latex_document ${latex_main_input} ${ARGN}) - - latex_copy_input_file(${LATEX_MAIN_INPUT}) - - foreach (bib_file ${LATEX_BIBFILES}) - latex_copy_input_file(${bib_file}) - endforeach (bib_file) - - if (LATEX_USE_BIBLATEX AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/biblatex.cfg) - latex_copy_input_file(biblatex.cfg) - set(LATEX_USE_BIBLATEX_CONFIG TRUE) - endif() - - foreach (input ${LATEX_INPUTS}) - latex_copy_input_file(${input}) - endforeach(input) - - latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.cls ${output_dir}) - latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.bst ${output_dir}) - latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.clo ${output_dir}) - latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.sty ${output_dir}) - latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.ist ${output_dir}) - latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.fd ${output_dir}) - - add_latex_targets_internal() - endif() -endfunction(add_latex_document) - -############################################################################# -# Actually do stuff -############################################################################# - -if(LATEX_BUILD_COMMAND) - set(command_handled) - - if("${LATEX_BUILD_COMMAND}" STREQUAL execute_latex) - latex_execute_latex() - set(command_handled TRUE) - endif() - - if("${LATEX_BUILD_COMMAND}" STREQUAL makeglossaries) - latex_makeglossaries() - set(command_handled TRUE) - endif() - - if("${LATEX_BUILD_COMMAND}" STREQUAL makenomenclature) - latex_makenomenclature() - set(command_handled TRUE) - endif() - - if("${LATEX_BUILD_COMMAND}" STREQUAL correct_synctex) - latex_correct_synctex() - set(command_handled TRUE) - endif() - - if("${LATEX_BUILD_COMMAND}" STREQUAL check_important_warnings) - latex_check_important_warnings() - set(command_handled TRUE) - endif() - - if(NOT command_handled) - message(SEND_ERROR "Unknown command: ${LATEX_BUILD_COMMAND}") - endif() - -else() - # Must be part of the actual configure (included from CMakeLists.txt). - latex_setup_variables() - latex_setup_targets() -endif() diff --git a/timing.typ b/timing.typ new file mode 100644 index 0000000..e914600 --- /dev/null +++ b/timing.typ @@ -0,0 +1,171 @@ +#import "common.typ": cetz, monotext, hex + +#let c = (len, ..args) => (type: "C", len: len, ..args.named()) +#let d = (len, label, ..args) => (type: "D", len: len, label: label, ..args.named()) +#let e = (len, ..args) => (type: "E", len: len, ..args.named()) +#let h = (len, ..args) => (type: "H", len: len, ..args.named()) +#let l = (len, ..args) => (type: "L", len: len, ..args.named()) +#let u = (len, ..args) => (type: "U", len: len, ..args.named()) +#let x = (len, ..args) => (type: "X", len: len, ..args.named()) +#let z = (len, ..args) => (type: "Z", len: len, ..args.named()) + +#let diagram = (..args, w_scale: 1.0, y_scale: 1.0, grid: false, fg: () => none) => { + cetz.canvas(length: 0.7em, { + import cetz.draw + let x_slope = 0.15 * w_scale; + let y_step = 2 * y_scale + let y_h = 1.0 * y_scale + let y_m = 0.5 * y_scale + let y_l = 0.0 * y_scale + draw.set-style(stroke: (thickness: 0.07em)) + + let resolve_level = (prev_state, event) => if event.type == "C" { + if prev_state.level == "H" { + "L" + } else if prev_state.level == "L" { + "H" + } + } else if ("L", "H", "E").contains(event.type) { + event.type + } else if ("Z", "X", "D", "U").contains(event.type) { + "M" + } + + let draw_event = (prev_state, event, next_type) => { + let x_start = if ("D", "U").contains(event.type) and prev_state.type != "" { x_slope } else { 0.0 } + let x_end = if ("D", "U").contains(next_type) { + event.len * w_scale + x_slope + } else { + event.len * w_scale + } + if event.type == "L" { + if prev_state.level == "H" { + draw.line((x_start, y_h), (x_start + x_slope, y_l), (x_end, y_l)) + } else if prev_state.level == "E" { + draw.line((x_start, y_h), (x_start + x_slope, y_l)) + draw.line((x_start, y_l), (x_end, y_l)) + } else if prev_state.level == "M" { + draw.line((x_start, y_m), (x_start + x_slope, y_l), (x_end, y_l)) + } else { + draw.line((x_start, y_l), (x_end, y_l)) + } + } else if event.type == "H" { + if prev_state.level == "L" { + draw.line((x_start, y_l), (x_start + x_slope, y_h), (x_end, y_h)) + } else if prev_state.level == "E" { + draw.line((x_start, y_l), (x_start + x_slope, y_h)) + draw.line((x_start, y_h), (x_end, y_h)) + } else if prev_state.level == "M" { + draw.line((x_start, y_m), (x_start + x_slope, y_h), (x_end, y_h)) + } else { + draw.line((x_start, y_h), (x_end, y_h)) + } + } else if event.type == "C" { + if prev_state.level == "L" { + draw.line((x_start, y_l), (x_start, y_scale), (x_end, y_scale)) + } else if prev_state.level == "H" { + draw.line((x_start, y_scale), (x_start, y_l), (x_end, y_l)) + } + } else if event.type == "E" { + if prev_state.level == "L" { + draw.line((x_start, y_l), (x_start + x_slope, y_h), (x_end, y_h)) + } else if prev_state.level == "M" { + draw.line((x_start, y_m), (x_start + x_slope, y_h), (x_end, y_h)) + } else { + draw.line((x_start, y_h), (x_end, y_h)) + } + if prev_state.level == "H" { + draw.line((x_start, y_h), (x_start + x_slope, y_l), (x_end, y_l)) + } else if prev_state.level == "M" { + draw.line((x_start, y_m), (x_start + x_slope, y_l), (x_end, y_l)) + } else { + draw.line((x_start, y_l), (x_end, y_l)) + } + } else if event.type == "X" { + let opacity = 100% - (if "opacity" in event { event.opacity } else { 100% }) + draw.line((x_start, y_m), (x_end, y_m), stroke: (paint: red.lighten(opacity))) + } else if event.type == "Z" { + let opacity = 100% - (if "opacity" in event { event.opacity } else { 100% }) + draw.line((x_start, y_m), (x_end, y_m), stroke: (paint: blue.lighten(opacity))) + } else if event.type == "D" or event.type == "U" { + let opacity = 100% - (if "opacity" in event { event.opacity } else { 100% }) + let fg = (if "stroke" in event { event.stroke } else { black }).lighten(opacity) + let fill = if event.type == "U" { gray } else if "fill" in event { event.fill.lighten(opacity) } else { none } + let left-open = prev_state.level == "" + let right-open = next_type == "" + if left-open and right-open { + if fill != none { + draw.rect((x_start, y_l), (x_end, y_h), stroke: none, fill: fill) + } + draw.line((x_start, y_h), (x_end, y_h), stroke: (paint: fg)) + draw.line((x_start, y_l), (x_end, y_l), stroke: (paint: fg)) + } else { + let points = () + if left-open { + points.push((x_start, y_h)) + points.push((x_end - x_slope, y_h)) + points.push((x_end, y_m)) + points.push((x_end - x_slope, y_l)) + points.push((x_start, y_l)) + } else if right-open { + points.push((x_end, y_h)) + points.push((x_start + x_slope, y_h)) + points.push((x_start, y_m)) + points.push((x_start + x_slope, y_l)) + points.push((x_end, y_l)) + } else { + points.push((x_start, y_m)) + points.push((x_start + x_slope, y_h)) + points.push((x_end - x_slope, y_h)) + points.push((x_end, y_m)) + points.push((x_end - x_slope, y_l)) + points.push((x_start + x_slope, y_l)) + points.push((x_start, y_m)) + } + draw.line(..points, stroke: (paint: fg), fill: fill) + } + draw.anchor("center", (event.len * w_scale / 2.0, y_m)) + if "label" in event { + draw.content("center", text(0.5em, fill: black.lighten(opacity), event.label)) + } + } + draw.translate((event.len * w_scale, 0)) + } + let lanes = args.pos().rev() + draw.group(name: "labels", { + for i in range(0, lanes.len()) { + let lane = lanes.at(i) + draw.content((0, i * y_step + 0.5), anchor: "right", lane.label) + } + }) + draw.group(name: "diagram", ctx => { + let (x, _, _) = cetz.coordinate.resolve(ctx, "labels.right") + draw.translate((x + 1, 0)) + draw.group(ctx => { + for i in range(0, lanes.len()) { + let lane = lanes.at(i) + draw.group(ctx => { + draw.anchor("left", (0.0, y_l)) + let prev_state = (level: "", type: "") + for i in range(lane.wave.len()) { + let event = lane.wave.at(i) + let next_type = if i + 1 < lane.wave.len() { lane.wave.at(i + 1).type } else { "" } + draw_event(prev_state, event, next_type) + prev_state.level = resolve_level(prev_state, event) + prev_state.type = event.type + } + draw.anchor("right", (0.0, y_h)) + if grid { + draw.on-layer(-1, { + draw.grid("left", "right", step: (x: 0.5 * w_scale, y: 0.5 * y_scale), stroke: (paint: gray.lighten(60%), thickness: 0.01em)) + }) + } + }) + draw.translate((0, y_step)) + } + }) + fg() + }) + }) +} +