// main_msx1_jp.oc: Ocala port of main_msx1_jp.asm
// GENERATED BY ocalaconv

// -- doc: cbios.txt
/* ************************************************************************* **

C-BIOS 0.29
===========

This software is a substitute BIOS which is can be used for running MSX
emulators. It currently supports only execution of cartridge image ("ROMs").
Before you use it, you should read and accept the license (see below).

On the C-BIOS web site, you can download newer versions, download the source
code, and report bugs.

http://cbios.sourceforge.net/

License
-------

Copyright (c) 2002-2005 BouKiCHi.  All rights reserved.
Copyright (c) 2003 Reikan.  All rights reserved.
Copyright (c) 2004-2006,2008-2010 Maarten ter Huurne.  All rights reserved.
Copyright (c) 2004-2006,2008-2011 Albert Beevendorp.  All rights reserved.
Copyright (c) 2004-2005 Patrick van Arkel.  All rights reserved.
Copyright (c) 2004,2010-2011 Manuel Bilderbeek.  All rights reserved.
Copyright (c) 2004-2006 Joost Yervante Damad.  All rights reserved.
Copyright (c) 2004-2006 Jussi Pitkänen.  All rights reserved.
Copyright (c) 2004-2007 Eric Boon.  All rights reserved.

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.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.

History
-------

ver 0.01  Initial
ver 0.02  2002-08-15(JST)
          * Added original font and drawing screen.
          * Added dump-mode.
          * Changed recognition method of cartridges
           to recognize cartridges taking priority.
ver 0.03  2002-08-19(JST)
          * Based on a suggestion from Ms.Okei,
           wrote 20h of BIOS(compare HL and DE).
           In the result, shooting game of a certain company became runnable
           more correctly.
           Thank Ms.Okei!!
ver 0.04  2002-08-20(JST)
          * Added initialize of FCC2h-FCC4h.
          * Added function of GTSTCK and GTTRIG temporarily.
          * Divided msxb.bin to halfs.
           doing combining/copying with setb.bat now.
ver 0.05  2002-08-27(JST)
          * Added INITGRP(only screen2), CHGMOD(graphic mode change routine),
           a routine calls H.STKE.
          * Rewrite memory recognition routine.
          * Some bug fixes.
          * Added sound test function.
ver 0.06  2002-09-01(JST)
          * Fixed around of color.
ver 0.07  2002-09-09(JST)
          * Added some sorts of keyboard routines.
          * Added joystich function to GTSTCK and GTTRIG.
ver 0.08  2002-09-12(JST)
          * Restructured memory initialize routine.
          * Added error display routine.
          * Fixed routine of finding kinds of cartridges.
          * Fixed using method of EXPTBL.
          * Added initialize of from RG8SAV to RG23SA.
          * Now return within disabled interrupt from ENASLT routine.
ver 0.09  2002-09-19(JST)
          * Made the rest half of font.
          * Improved key input routine.
          * Added CHPUT. With it, rewrote display routine.
          * Fixed init_grp.
          * Changed filenames to CBIOS.ROM, CBIOS_SUB.ROM.
ver 0.10  2002-09-20(JST)
          * Fixed indent.
          * and so on...
ver 0.10a 2002-09-22(JST)
          * Fixed license.
          * Added support of ROMs in page3.
ver 0.11  2002-09-22(JST)
          * Small fix in init_sc5.
ver 0.12beta
          2002-09-25(JST)
          * Added test routine for disk access. need DISK.ROM.
          * Added init_sc7.
          * Improved ENASLT. now finding cartridge uses ENASLT.
          * Improved RAM detection.
ver 0.12  2002-09-27(JST)
          * Changed finding cartridge again.
          * Changed screen mode of cartridge running time.
          * Fixed keyboard routine.
          * Fixed stick routine against to interrupt.
ver 0.13  2002-10-02(JST)
          * Based on info from Mr.Maarten (a member of openMSX developers),
           fixed around of SCREEN 5.
           For detail, switching line numbers,
           temporary treatment for a bug of reading from VDP status register,
           and so on.
ver 0.14  2002-10-10(JST)
          * Rewrote comments in source within Japanese.
ver 0.15  2003-02-26(JST)
          * Rewrote some of comments back to English again.
          * Fixed non-assemblable condition becauseof lack of font file.
          * Changed filename, some of label name, strings and so on.
ver 0.16  2003-04-16(JST)
          * Separated sound test from source. (Disabled)
ver 0.16a 2003-06-01(JST)
          * CHGMOD: When screen0/1, now load font to VRAM.
          * CHPUT:  Now support also screen1 not only screen0.
ver 0.16b 2003-08-10(JST)
          * Added entry: INITXT, INIT32.
           These were exist only as internal routine of CHGMOD.
          * INITXT, INIT32: Fixed screen clear failure.
          * CHPUT: Fixed scroll failure.
ver 0.17  2003-08-10(JST)
          * Changed LICENSE.
           New LICENSE will be suitable in various situations.
           e.g. use as a firmware for hand-made hardware.
ver 0.18  2004-12-18(CET)
          * First release since moving to SourceForge.
          * Much improved support for MSX2 games.
          * Graphical boot logo.
          * Included machine config files for several MSX emulators.
          * Various bug fixes.
ver 0.19  2004-12-24(CET)
          * Added support for SCREEN4 and SCREEN8.
          * Added support for clock chip.
          * Added support for palette. This fixes a lot of wrong colours.
          * Stubbed many calls: non-implemented calls print their name on the
            openMSX debugdevice (if present).
          * Various bug fixes.
ver 0.20  2005-02-09(CET)
          * Added an MSX2+ configuration, which includes V9958 and MSX MUSIC.
          * Separate main ROMs for MSX1/MSX2/MSX2+.
          * Implemented several MSX2 specific routines, including BLT*.
          * Display is disabled when switching to a different screen mode.
          * Improved CHPUT a lot; implemented control and escape codes.
          * Rewrote key buffering; fixes bug of keys being repeated.
          * New boot logo, even cooler than the previous one.
          * New font, placed at a fixed address so all games can find it.
          * Started work on a disk ROM, but it is not functional yet, so it
            is not enabled in the configurations.
          * Stubbed all non-implemented calls.
          * Various bug fixes.
ver 0.21  2005-06-07(CET)
          * Fixed RuMSX configuration files, thanks to Rudolf Lechleitner.
          * Rewrote ROM search code; now all ROMs are recognized.
            Also a clear error message is printed for BASIC ROMs.
          * New boot logo for MSX2 and MSX2+.
          * Changed boot sequence:
            Show logo, switch to SCREEN 1 and search for ROMs.
          * Improved video code; fixes several games.
          * Various bug fixes.
ver 0.22  2008-12-27(CET)
          * Use separate logo ROM to save space in the main ROM.
          * Set lower bits of PSG reg 15 before reading joystick trigger status.
          * Improved RAM search.
          * Many new routines implemented and existing implementations made
            more complete, especially character I/O and bitmap graphics.
          * Added lots of documentation to system variables.
          * Added support for GNU assembler.
          * Various bug fixes.
ver 0.23  2009-01-04(CET)
          * Updated blueMSX configuration files, thanks to Benoît Delvaux.
          * Fixed version reported by MSX1 logo ROM.
          * Fixed several video routines so they work on MSX1 VDPs (TMS99xx).
          * A couple of other bug fixes.
ver 0.24  2010-05-24(CET)
          * VRAM size is now properly checked, fixing R-Type's V9938 detection.
          * C-BIOS doesn't lie anymore about the interrupt frequency.
          * Don't di; halt when no ROM is found, the warning in openMSX may be
            confusing
          * A few minor bug fixes and tweaks.
ver 0.25  2011-02-01(CET)
          * C-BIOS now offers localized versions in the flavours INT (default),
            JP and BR.
          * Bug fixes for compatibility with Mirai, Family Billiards.
          * A couple of other bug fixes.
          * This version only compiles with Pasmo 0.5.3, due to lack of
            standards in assembler directives...
ver 0.26  2014-11-02(CET)
          * Restored support to compile with tniASM (v1.0 Beta 17 or higher)
          * Moved to git, which means a.o.: archived changelog.txt, use git log
            from now on
          * Fixed VDP VRAM access timing for MSX1 VDP's
          * Update openMSX configurations to the new structure
          * Fixed bug blueMSX configurations
          * Fixed build on Mac OS X and add support for "make dist"
ver 0.27  2014-11-05(CET)
          * Fixed bug (regression) in filvrm on non-MSX1-VDP's
          * Fixed some small bugs in openMSX configs
          * Fixed line endings of this file
ver 0.28  2017-07-30(CEST)
          * Fixed bug that prevented brackets and a few other keys from
            generating characters when pressed
ver 0.29  2018-02-18(CET)
          * Removed NLMSX configs. We don't think anyone is interested anymore
          * Made generic international ROM config 60Hz and added an EU variant
            at 50Hz default interrupt frequency
ver 0.29a 2018-09-23(CEST)
          * Fixed metadata in openMSX configs for EU machines

Special Thanks
--------------

People uploading MSX information to the internet.
People developing any kind of emulators.
All users.

Font edit tool:
 Gameboy Tile Designer version 2.2
 Copyright H. Mulder 1999

** ************************************************************************* */

arch z80
include "z80.oc"
link { org 0 0x8000 LINK/FILL; merge text _ }

macro org>(addr) { fill (%=addr - __PC__) }
struct byteword { b byte ; w word }

flat!
section text ={
    // -- begin: main_msx1_jp.asm

    // ; C-BIOS main ROM for a Japanese MSX1 machine
    // ;
    // ; Copyright (c) 2005 Maarten ter Huurne.  All rights reserved.
    // ; Copyright (c) 2005 Joost Yervante Damad.  All rights reserved.
    // ; Copyright (C) 2005 BouKiCHi. All rights reserved.
    // ; Copyright (C) 2008 Eric Boon. All rights reserved.
    // ;
    // ; 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.
    // ;
    // ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
    // ;

    // -- begin: hardware.asm

    // ; C-BIOS hardware related declarations
    // ;
    // ; Copyright (c) 2002-2005 BouKiCHi.  All rights reserved.
    // ; Copyright (c) 2003 Reikan.  All rights reserved.
    // ; Copyright (c) 2004-2005 Maarten ter Huurne.  All rights reserved.
    // ; Copyright (c) 2004 Manuel Bilderbeek.  All rights reserved.
    // ; Copyright (c) 2004-2006 Albert Beevendorp.  All rights reserved.
    // ; Copyright (c) 2004-2005 Joost Yervante Damad.  All rights reserved.
    // ;
    // ; 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.
    // ;
    // ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
    // ;

    // ;---------------------------------------------------
    // ; I/O ports

    const DBG_CTRL = 0x2e                // ; openMSX debugdevice control (mode)
    const DBG_DATA = 0x2f                // ; openMSX debugdevice data
    const PRN_STAT = 0x90                // ; printer status
    const PRN_DATA = 0x91                // ; printer data
    const VDP_DATA = 0x98                // ; VDP data port (VRAM read/write)
    const VDP_ADDR = 0x99                // ; VDP address (write only)
    const VDP_STAT = 0x99                // ; VDP status (read only)
    const VDP_PALT = 0x9a                // ; VDP palette latch (write only)
    const VDP_REGS = 0x9b                // ; VDP register access (write only)
    const PSG_REGS = 0xa0                // ; PSG register write port
    const PSG_DATA = 0xa1                // ; PSG value write port
    const PSG_STAT = 0xa2                // ; PSG value read port
    const PSL_STAT = 0xa8                // ; slot status
    const KBD_STAT = 0xa9                // ; keyboard status
    const GIO_REGS = 0xaa                // ; General IO Register
    const PPI_REGS = 0xab                // ; PPI register
    const RTC_ADDR = 0xb4                // ; RTC address
    const RTC_DATA = 0xb5                // ; RTC data
    const SYSFLAGS = 0xf4                // ; MSX2+ System flags,
                                         // ; preserved after reset
                                         // ; bit 5: CPU boot mode (1=R800)
                                         // ; bit 7: Boot method
                                         // ;        (1=soft boot, no logo)

    const MAP_REG1 = 0xfc                // ; memory mapper: bank in $0000-$3FFF
    const MAP_REG2 = 0xfd                // ; memory mapper: bank in $4000-$7FFF
    const MAP_REG3 = 0xfe                // ; memory mapper: bank in $8000-$BFFF
    const MAP_REG4 = 0xff                // ; memory mapper: bank in $C000-$FFFF

    // ;---------------------------------------------------
    // ; memory mapped I/O

    const SSL_REGS = 0xffff              // ; secondary slot select registers

    // ;---------------------------------------------------
    // ; Constants used to define which hardware the BIOS will run on.
    // ; Used by the main_<model>.asm sources.

    // ; VDP models:
    const TMS99X8 = 0x9918
    const V9938 = 0x9938
    const V9958 = 0x9958

    // ; MSX models:
    const MODEL_MSX1 = 0
    const MODEL_MSX2 = 1
    const MODEL_MSX2P = 2
    const MODEL_MSXTR = 3
    const MODEL_SUBROM = 4

    // ; Locales:
    // ; -- ID byte 0
    const LOCAL_CHSET_JP = 0x00
    const LOCAL_CHSET_US = 0x01
    const LOCAL_CHSET_KO = 0x02

    // ; There are charsets which pretend to be INT, but are not... For now only BR:
    const LOCAL_CHSET_VAR_NONE = 0x00
    const LOCAL_CHSET_VAR_BR = 0x01
    const LOCAL_DATE_YMD = 0x00
    const LOCAL_DATE_MDY = 0x10
    const LOCAL_DATE_DMY = 0x20
    const LOCAL_INT_60HZ = 0x00
    const LOCAL_INT_50HZ = 0x80

    // ; -- ID byte 1
    const LOCAL_KBD_JP = 0x00
    const LOCAL_KBD_US = 0x01
    const LOCAL_KBD_FR = 0x02
    const LOCAL_KBD_UK = 0x03
    const LOCAL_KBD_DE = 0x04
    const LOCAL_BASIC_JP = 0x00
    const LOCAL_BASIC_US = 0x01

    // ; BOOLEAN VALUES
    const YES = 1
    const NO = 0

    // ; vim:ts=8:expandtab:filetype=z8a:syntax=z8a:
    // -- end: hardware.asm
    const VDP = TMS99X8
    const MODEL_MSX = MODEL_MSX1

    // ; -- japanese config
    const LOCALE_CHSET = LOCAL_CHSET_JP
    const LOCALE_CHSET_VAR = LOCAL_CHSET_VAR_NONE
    const LOCALE_DATE = LOCAL_DATE_YMD
    const LOCALE_INT = LOCAL_INT_60HZ
    const LOCALE_KBD = LOCAL_KBD_JP
    const LOCALE_BASIC = LOCAL_BASIC_JP
    const COLOR_FORE = 15
    const COLOR_BACK = 4
    const COLOR_BORDER = 7
    const CALL_SUB = NO

    // -- begin: main.asm

    // ; C-BIOS main ROM
    // ;
    // ; Copyright (c) 2002-2005 BouKiCHi.  All rights reserved.
    // ; Copyright (c) 2003 Reikan.  All rights reserved.
    // ; Copyright (c) 2004-2005 Maarten ter Huurne.  All rights reserved.
    // ; Copyright (c) 2004-2009 Albert Beevendorp.  All rights reserved.
    // ; Copyright (c) 2004 Manuel Bilderbeek.  All rights reserved.
    // ; Copyright (c) 2004-2005 Joost Yervante Damad.  All rights reserved.
    // ; Copyright (c) 2004-2005 Jussi Pitkänen.  All rights reserved.
    // ; Copyright (c) 2006-2007 Eric Boon.  All rights reserved.
    // ;
    // ; 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.
    // ;
    // ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
    // ;

    // -- begin: systemvars.asm

    // ;
    // ; C-BIOS system variable declarations
    // ;
    // ; Copyright (c) 2002-2003 BouKiCHi.  All rights reserved.
    // ; Copyright (c) 2003 Reikan.  All rights reserved.
    // ; Copyright (c) 2004-2005 Maarten ter Huurne.  All rights reserved.
    // ; Copyright (c) 2004 Manuel Bilderbeek.  All rights reserved.
    // ; Copyright (c) 2004-2006 Joost Yervante Damad.  All rights reserved.
    // ; Copyright (c) 2004-2005 Albert Beevendorp.  All rights reserved.
    // ; Copyright (c) 2005 Jussi Pitkänen.  All rights reserved.
    // ;
    // ; 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.
    // ;
    // ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
    // ;

    // ;-------------------
    // ; help function area
    // ;-------------------
    // ; Note: Functions defined in "main.asm" are disabled here.

    // ; F380-F384: interslot read
    // ;RDPRIM:         equ     $F380

    // ; F385-F38B: interslot read
    // ;WRPRIM:         equ     $F385

    // ; F38C-F399: interslot call
    // ;CLPRIM:         equ     $F38C

    // ; F39A-F3AD: workarea for the DEF USR statement
    // ; this area is initialized with the 10 times the value $475A, which gives
    // ; the error 'Syntax Error'
    const USRTAB = 0xf39a

    // ;----------------------
    // ; screen parameter area
    // ;----------------------

    // ; F3AE: # of positions on a line in SCREEN 0 (ini:39)
    const LINL40 = 0xf3ae

    // ; F3AF: # of positions on a line in SCREEN 1 (ini:29)
    const LINL32 = 0xf3af

    // ; F3B0: # of actually used positions in the current screenmodus (ini:39)
    const LINLEN = 0xf3b0

    // ; F3B1: # of used lines on screen (ini:24)
    const CRTCNT = 0xf3b1

    // ; F3B2: # of positions within a tabulator-column (ini:14)
    const CLMLST = 0xf3b2

    // ; F3B3-F3B4: BASE(0): name table address for SCREEN 0 (ini:$0000)
    // ; used to initialize NAMBAS when SCREEN 0 is activated
    const TXTNAM = 0xf3b3

    // ; F3B5-F3B6: BASE(1): color table address for SCREEN 0, unused? (ini:$0000)
    const TXTCOL = 0xf3b5

    // ; F3B7-F3B8: BASE(2): pattern table address for SCREEN 0 (ini:$0800)
    // ; used to initialize CGPBAS when SCREEN 0 is activated
    const TXTCGP = 0xf3b7

    // ; F3B9-F3BA: BASE(3): sprite attribute table address for SCREEN 0, unused (ini:$0000)
    // ; used to initialize ATRBAS when SCREEN 0 is activated
    const TXTATR = 0xf3b9

    // ; F3BB-F3BC: BASE(4): sprite pattern table address for SCREEN 0, unused (ini:$0000)
    // ; used to initialize PATBAS when SCREEN 0 is activated
    const TXTPAT = 0xf3bb

    // ; F3BD-F3BE: BASE(5): nametable address for SCREEN 1 (ini:$1800)
    // ; used to initialize NAMBAS when SCREEN 1 is activated
    const T32NAM = 0xf3bd

    // ; F3BF-F3C0: BASE(6): color table address for SCREEN 1 (ini:$2000)
    const T32COL = 0xf3bf

    // ; F3C1-F3C2: BASE(7): pattern table address for SCREEN 1 (ini:$0000)
    // ; used to initialize CGPBAS when SCREEN 1 is activated
    const T32CGP = 0xf3c1

    // ; F3C3-F3C4: BASE(8): sprite attribute table address for SCREEN 1 (ini:$1B00)
    // ; used to initialize ATRBAS when SCREEN 1 is activated
    const T32ATR = 0xf3c3

    // ; F3C5-F3C6: BASE(9): sprite pattern table address for SCREEN 1 (ini:$0800)
    // ; used to initialize PATBAS when SCREEN 1 is activated
    const T32PAT = 0xf3c5

    // ; F3C7-F3C8: BASE(10): name table address for SCREEN 2 (ini:$1800)
    // ; used to initialize NAMBAS when SCREEN 2 is activated
    const GRPNAM = 0xf3c7

    // ; F3C9-F3CA: BASE(11): color table address for SCREEN 2 (ini:$2000)
    const GRPCOL = 0xf3c9                // ; Screen2 Color

    // ; F3CB-F3CC: BASE(12): pattern table address for SCREEN 2 (ini:$0000)
    // ; used to initialize CGPBAS when SCREEN 2 is activated
    const GRPCGP = 0xf3cb

    // ; F3CD-F3CE: BASE(13): sprite attribute table address for SCREEN 2 (ini:$1B00)
    // ; used to initialize ATRBAS when SCREEN 2 is activated
    const GRPATR = 0xf3cd

    // ; F3CF-F3D0: BASE(14): sprite pattern table address for SCREEN 2 (ini:$3800)
    // ; used to initialize PATBAS when SCREEN 2 is activated
    const GRPPAT = 0xf3cf

    // ; F3D1-F3D2: BASE(15): name table address for SCREEN 3 (ini:$0800)
    // ; used to initialize NAMBAS when SCREEN 3 is activated
    const MLTNAM = 0xf3d1

    // ; F3D3-F3D4: BASE(16): color table address for SCREEN 3 (ini:$0000)
    // ; the color table is unused in SCREEN 3
    const MLTCOL = 0xf3d3

    // ; F3D5-F3D6: BASE(17): pattern table address for SCREEN 3 (ini:$0000)
    // ; used to initialize CGPBAS when SCREEN 3 is activated
    const MLTCGP = 0xf3d5

    // ; F3D7-F3D8: BASE(18): sprite attribute table address for SCREEN 3 (ini:$1B00)
    // ; used to initialize ATRBAS when SCREEN 3 is activated
    const MLTATR = 0xf3d7

    // ; F3D9-F3DA: BASE(19): sprite pattern table address for SCREEN 3 (ini:$3800)
    // ; used to initialize PATBAS when SCREEN 3 is activated
    const MLTPAT = 0xf3d9

    // ; F3DB: keyclick when a key is pressed: 0: no, 1: yes (ini: 1)
    // ; SCREEN ,,n will write to this address
    const CLIKSW = 0xf3db

    // ; F3DC: line where the cursor is located
    // ; starts to count at 1 for the topmost line
    const CSRY = 0xf3dc

    // ; F3DD: column where the cursor is located
    // ; starts to count at 1 for the leftmost column
    const CSRX = 0xf3dd

    // ; F3DE: function key definition shown: 0: no, -1: yes
    // ; Note: MSX BIOS will mess up end-of-screen if this variable contains
    // ;       something other than $00 or $FF.
    const CNSDFG = 0xf3de

    // ; F3DF-D3E6: storage for the last written value towards VDP registers 0 till 7
    // ; this is needed because these registers are write only
    const RG0SAV = 0xf3df
    const RG1SAV = 0xf3e0
    const RG2SAV = 0xf3e1
    const RG3SAV = 0xf3e2
    const RG4SAV = 0xf3e3
    const RG5SAV = 0xf3e4
    const RG6SAV = 0xf3e5
    const RG7SAV = 0xf3e6

    // ; F3E7: last read value of VDP register 8
    const STATFL = 0xf3e7

    // ; F3E8: information about the joystick and space bar
    // ; 7 6 5 4 3 2 1 0
    // ; | | | |       +-- Space bar, trig(0) (0 = pressed)
    // ; | | | +---------- Stick 1, Trigger 1 (0 = pressed)
    // ; | | +------------ Stick 1, Trigger 2 (0 = pressed)
    // ; | +-------------- Stick 2, Trigger 1 (0 = pressed)
    // ; +---------------- Stick 2, Trigger 2 (0 = pressed)
    const TRGFLG = 0xf3e8

    // ; F3E9: code for the standard foreground color (ini:15)
    const FORCLR = 0xf3e9

    // ; F3EA: code for the standard background color (ini:4)
    const BAKCLR = 0xf3ea

    // ; F3EB: code for the standard border color (ini:7)
    const BDRCLR = 0xf3eb

    // ; F3EC-F3EE: Jump instruction used by Basic LINE command.
    // ; The routines used are: RIGHTC, LEFTC, UPC and DOWNC
    const MAXUPD = 0xf3ec

    // ; F3EF-F3F1: Jump instruction used by Basic LINE command.
    // ; The routines used are: RIGHTC, LEFTC, UPC and DOWNC
    const MINUPD = 0xf3ef

    // ; F3F2: working color, as used for graphical operations
    // ; normally equals to the foreground color (ini:15)
    const ATRBYT = 0xf3f2

    // ; F3F3-F3F4: starting value of the address of the queue-table
    // ; the queue-table contains 4 queue's: 3 for sound and one for RS232
    // ; (ini: QUETAB ($F959))
    const QUEUES = 0xf3f3

    // ; F3F5: CLOAD flag =0 when CLOAD =255 when CLOAD?
    const FRCNEW = 0xf3f5

    // ; F3F6: VDP-interupt counter that counts from 3 to 0, when it reaches zero, the
    // ; keyboard matrix is scanned, and the counters is reset at 3
    const SCNCNT = 0xf3f6

    // ; F3F7: key repeat counter. Runs from 13 to 0, and is changed when SCNCNT is changed
    // ; if the key remained the same. If it reaches 0, keyrepetition starts. If another key
    // ; is pressed the value is reset at 13.
    const REPCNT = 0xf3f7

    // ; F3F8-F3F9: first free space in the inputbuffer of the keyboard
    // ; everytime a key is added to the inputbuffer, this address is incremented,
    // ; when it equals to GETPNT, the buffer is full
    // ; the buffer is located at KEYBUF
    const PUTPNT = 0xf3f8

    // ; F3FA-F3FB: address in inputbuffer of first character that is not yet read
    // ; everytime a key is read from the buffer it is incremented
    // ; the buffer is located at KEYBUF
    const GETPNT = 0xf3fa

    // ; F3FC-F400: memory area for tape system parameters for 1200 baud
    // ; F3FC: length of  low signal for 0     (ini:83)
    // ; F3FD: length of high signal for 0     (ini:92)
    // ; F3FE: length of  low signal for 1     (ini:38)
    // ; F3FF: length of high signal for 1     (ini:45)
    // ; F400: length of synchronization block (ini:15)
    const CS120 = 0xf3fc

    // ; F401-F405: memory area for tape system parameters for 1200 baud
    // ; F401: length of  low signal for 0     (ini:37)
    // ; F402: length of high signal for 0     (ini:45)
    // ; F403: length of  low signal for 1     (ini:14)
    // ; F404: length of high signal for 1     (ini:22)
    // ; F405: length of synchronization block (ini:31)
    const CS240 = 0xf401

    // ; F406-F407: lenghts of signal for 0 for the current speed of the tape system
    // ; either equal to the content of F3FC-F3FD or the content of F401-F402
    // ; (ini: 83, 92)
    const LOW_ = 0xf406                  // ; real name: LOW, but doesn't compile?

    // ; F408-F409: lenghts of signal for 1 for the current speed of the tape system
    // ; either equal to the content of F3FE-F3FF or the content of F403-F404
    // ; (ini: 38, 45)
    const HIGH_ = 0xf408                 // ; real name: HIGH, but doesn't compile?

    // ; F40A: lenghts of synchronization block for the current speed of the tape system
    // ; either equal to the content of F400 or the content of F405 (ini: 15)
    const HEADER = 0xf40a

    // ; F40B-F40C: standard setting for the height/width aspect of the
    // ; BASIC statement CIRCLE; only the byte in F40B is actually used
    // ; If ASPECT2 is larger then 255, the value of F40B is the number of horizontal
    // ; dots per 256 verical dots of the radius (ini:$0100)
    // ; ! not verified :)
    const ASPCT1 = 0xf40b

    // ; F40D-F40E: standard setting for the height/width aspect of the
    // ; BASIC statement CIRCLE; If ASPCT2 is smaller then 512, then ASPCT2 is the
    // ; number of vertical dots per 256 horizontal dots of the radius (ini:$0100)
    // ; ! not verified :)
    const ASPCT2 = 0xf40d

    // ; F40F-F413: work area for the BASIC statement RESUME NEXT
    // ; contains a fake end of basic program
    // ; (ini: 58, 0, 0, 0, 0)
    const ENDPRG = 0xf40f

    // ; F414: errornumber of last error that happened while executing a BASIC program
    // ; (ini:0)
    const ERRFLG = 0xf414

    // ; F415: number of characters in the writebuffer of the printer that still
    // ; need printing
    const LPTPOS = 0xf415

    // ; F416: switch indicating if output should be screen or printer
    // ; (think LIST vs LLIST) (ini:0) values: 0: screen, 1: printer
    const PRTFLG = 0xf416

    // ; F417: switch indicating if hooked up printer is an MSX printer or not
    // ; values: 0: MSX-Printer, 1: no MSX-Printer
    // ; if the printer is no MSX-Printer, non-ASCII (>=128) characters are replaced
    // ; by spaces before sending them to the printer (ini: 0)
    const NTMSXP = 0xf417

    // ; F418: switch indicating of printing routines should use raw-mode or
    // ; should convert:
    // ; =0 to convert tabs and unknown characters to spaces and remove graphical headers
    // ; =1 to send data just like it gets it (ini: 0)
    // ; if RAWPRT is 1, the value if NTMSXP is ignored
    const RAWPRT = 0xf418

    // ; ---------------------------
    // ; basic interpreter work area
    // ; ---------------------------

    // ; F419-F41A: work area for the BASIC command VAL: contains address of character that
    // ; has temporarely been replaced by O by VAL
    const VLZADR = 0xf419

    // ; F41B: work area for the BASIC command VAL: contains the character originally at
    // ; the location of VLZADR
    const VLZDAT = 0xf41b

    // ; F41C-F41D: line number of current BASIC line being executed, in direct modus this
    // ; contains $FFFF (ini:$FFFF)
    const CURLIN = 0xf41c

    // ; F41E: error detection prefix for KBUF, always contains ":"
    // ; originally undocumented :)
    const KBFMIN = 0xf41e

    // ; F41F-F55C: workarea for coding basic rules that have been typed in direct modus
    // ; this are contains the code for the line interpreted in direct modus
    const KBUF = 0xf41f

    // ; F55D: byte used as first byte of BUF for input statements, giving them always
    // ; an extra ',' (ini:44 == ',')
    const BUFMIN = 0xf55d

    // ; F55E-F65F: used in direct modus to store the ASCII codes of the line, or simulary
    // ; for INPUT or LINE INPUT BASIC statements
    const BUF = 0xf55e

    // ; F562-F570: used by bitblit routines to store the register data
    const SX = 0xf562
    const SY = 0xf564
    const DX = 0xf566
    const DY = 0xf568
    const NX = 0xf56a
    const NY = 0xf56c
    const CDUMMY = 0xf56e
    const ARG_ = 0xf56f
    const L_OP = 0xf570

    // ; F660: last usable byte of BUF
    const ENDBUF = 0xf660

    // ; F661: number of column of last written character on the screen
    const TTYPOS = 0xf661

    // ; F662: switch indicating during variable lookup for arrays, if this has not already
    // ; been done for a DIM BASIC statement
    const DIMFLG = 0xf662

    // ; F663: workarea for evaluation of expressions; contains type of last evaluated
    // ; expression; the value of the expression is in DAC, possible values of VALTYP:
    // ; 2: integer
    // ; 3: string
    // ; 4: normal real
    // ; 8: double real
    const VALTYP = 0xf663

    // ; F664: workarea for coding of BASIC statements. switch indicating if keywords have
    // ; to be encoded or not. E.g. in DATA fields they should not be encoded
    // ; 0: encoding on, 1: encoding off
    const DORES = 0xf664

    // ; F665: workarea for coding of BASIC statements. swithc indication of numbers have to be
    // ; encoded; values: $0: encode as const, $1: encode as line number, $FF: do not encode
    const DONUM = 0xf665

    // ; F666-F667: work area for evaluation of expressions: contains address of first character
    // ; after the code of the last evaluated expression
    const CONTXT = 0xf666

    // ; F668: work area for evaluation of expressions: contains information byte about the
    // ; encoding of the last evaluated constant number; value of this constant is in CONLO
    // ; values:
    // ; $0B: octal (2 bytes)
    // ; $0C: hexadecimal (2 bytes)
    // ; $0F: decimal 0<=value<256 (1 byte)
    // ; $11-$1B: short encoding for 0->10
    // ; $1C: decimal (2bytes, 2s-compliment)
    // ; $26: $42 binary as ASCII
    // ; $0E: line number
    // ; $0D: line pointer
    // ; $1D: normal real (1 byte exp, 3 bytes BCD)
    // ; $1F: double real (1 byte exp, 7 bytes BCD)
    const CONSAV = 0xf668

    // ; F669: work area for evaluation of expressions: contains type of last evaluated number
    // ; constant; the value is in CONLO, for values of CONTYP, see VALTYP
    // ; Strings are never contant in BASIC!
    const CONTYP = 0xf669

    // ; F66A-F671: work area for evaluation of expressions: contains the value of the last
    // ; evaluated number contant; value starts at F66A, and takes bytes as needed for the type
    const CONLO = 0xf66a

    // ; F672-F673: upper limit of memory area reserved for strings, contains the upper address
    // ; that is allowed to be used
    const MEMSIZ = 0xf672

    // ; F674-F675: top of stack; also first byte below string area
    const STKTOP = 0xf674

    // ; F676-F677: start address of current basic program, set at initialization, and
    // ; not changed by OS (ini:$8001)
    const TXTTAB = 0xf676

    // ; F678-F679: address of first unused string-descriptor in TEMPST
    // ; (ini:value of TEMPST)
    const TEMPPT = 0xf678

    // ; F67A-F697: work area for evaluation of string expressions; this area has space
    // ; for 10 string descriptors of 3 bytes; these can be used for temporarely results
    // ; of string arythmetics
    const TEMPST = 0xf67a

    // ; F698-F69A: work area for evaluation of string expressions; this contains the
    // ; string descriptor of the intermediate result
    const DSCTMP = 0xf698

    // ; F69B-F69C: first address within the string memory area that is still free
    // ; the string area is filled backwards, soo the lower the value, the less space
    // ; remains (ini: value of MEMSIZ)
    const FRETOP = 0xf69b

    // ; F69D-F69E: temporarely storage for adminstration of the basic interpreter
    const TEMP3 = 0xf69d

    // ; F69F-F6A0: temporarely storage for garbage collection
    const TEMP8 = 0xf69f

    // ; F6A1-F6A2: address of first byte in BASIC-code after last FOR statement
    const ENDFOR = 0xf6a1

    // ; F6A3-F6A4: line number of last used line of DATA statements
    const DATLIN = 0xf6a3

    // ; F6A5: switch indicating if a variable is allowed to be an array variable.
    // ; This is e.g. not allowed for the loop variable of a FOR statement
    // ; 0 = allowed, 1 = not allowed
    const SUBFLG = 0xf6a5

    // ; F6A6: switch indicating if currently a READ or INPUT statement is being executed
    const FLKINP = 0xf6a6

    // ; F6A7-F6A8: temporarely storage for adminstration of the basic interpreter
    const TEMP = 0xf6a7

    // ; F6A9: switch indicating if there are still linenumber constants in the BASIC code
    // ; that are encoded as pointers?
    const PTRFLG = 0xf6a9

    // ; F6AA: switch indication if currently an AUTO statement is active
    // ; 0 = no auto, 1 = auto
    const AUTFLG = 0xf6aa

    // ; F6AB-F6AC: last generated AUTO line number
    const AUTLIN = 0xf6ab

    // ; F6AD-F6AE: last used AUTO increment
    const AUTINC = 0xf6ad

    // ; F6AF-F6B0: work area of the error system; contains address of first byte
    // ; of statement currently being executed
    const SAVTXT = 0xf6af

    // ; F6B1-F6B2: work area of the error system; contains address of the stack
    // ; before executing of the current statement started
    const SAVSTK = 0xf6b1

    // ; F6B3-F6B4: line number of last failed line
    const ERRLIN = 0xf6b3

    // ; F6B5-F6B6: line number of last used (changed, listed, added) line
    const DOT = 0xf6b5

    // ; F6B7-F5B8: work area of the error system; contains the address of the first
    // ; byte of the statement that last failed; on failure it is stored with the
    // ; content of SAVTXT
    const ERRTXT = 0xf6b7

    // ; F6B9-F6BA: work area of the error system; contains the line number where
    // ; execution should go to on error (as in basic: ON ERROR GOTO x)
    const ONELIN = 0xf6b9

    // ; F6BB-F6BC: work area of the error system; indication if the interpreter is
    // ; currently executing an error catch routine
    // ; 0 = no, FF = yes
    const ONEFLG = 0xf6bb

    // ; F6BC-F6BD: temporarely storage for the interpreter
    const TEMP2 = 0xf6bc

    // ; F6BE-F6BF: line number of last program break, reset at 0 at any program change
    const OLDLIN = 0xf6be

    // ; F6C0-F6C1: address of first statement that is not executed due to a break
    const OLDTXT = 0xf6c0

    // ; F6C2-F6C3: begin address of storage of basic variables and function descriptors;
    // ; possibly adjusted when program changes in size
    const VARTAB = 0xf6c2

    // ; F6C4-F6C5: begin address of array variables; possibly adjusted when program
    // ; changes size or more variables are allocated
    const ARYTAB = 0xf6c4

    // ; F6C6-F6C7: address of first free byte not used for storage of code or variables
    // ; (ini: $8003)
    const STREND = 0xf6c6

    // ; F6C8-F6C9: address where data needs to be searched at next READ statement
    const DATPTR = 0xf6c8

    // ; F6CA-F6E3: table with variable types, one for each letter in the alphabet
    // ; possible values:
    // ;       2 = integer     3 = string      4 = single      8 = double
    const DEFTBL = 0xf6ca

    // ; F6E4-F7B4: work area for execution of self defined functions

    // ; F6E4-F6E5: contains address ; of previous parameter block on the stack;
    // ; needed for garbage collection
    const PRMSTK = 0xf6e4

    // ; F6E6-F6E7:  amount of valid bytes in PARM1
    const PRMLEN = 0xf6e6

    // ; F6E8-F74B: contains definitions of the variables in the parameter lists
    // ; of self defined functions
    const PARM1 = 0xf6e8

    // ; F74C-F74D: previous value of PRMSTK
    const PRMDRV = 0xf74c

    // ; F74E-F74F: number of valid bytes in PARM2
    const PRMLN2 = 0xf74e

    // ; F750-F7B3: area used for calculation of values that end up in PARM1
    const PARM2 = 0xf750

    // ; F7B4: switch indicating of while searching a variable name PARM1 has
    // ; been looked at; 0 = no, 1 = yes
    const PRMFLG = 0xf7b4

    // ; F7B5-F7B6: address of first byte where it is no longer needed to search
    // ; for a variable name; it is equal to ARYTAB when the normal variable area
    // ; is searched, and equal to PARM1+PRMLEN when PARM1 is searched
    const ARYTA2 = 0xf7b5

    // ; F7B7-F7B8: switch indicating iif PARM1 contains a valid parameter block
    // ; 0 = no, 1 = yes
    const NOFUNS = 0xf7b7

    // ; F7B8-F7B9: temporarely memory used while searching parameter blocks on
    // ; the stack
    const TEMP9 = 0xf7b8

    // ; F7BA-F7BB: counter of the nesting-dept of the function being evaluated
    const FUNACT = 0xf7ba

    // ; F7BC-F7C3: work area when executing the SWAP statement; the first variable
    // ; is stored here
    const SWPTMP = 0xf7bc

    // ; F7C4: switch indicating if TRON is on; 0 = off, >0 = on
    const TRCFLG = 0xf7c4

    // ; F7C5-F7F4: workarea when executing numeric operators
    const FBUFFR = 0xf7c5
    const DECTMP = 0xf7f0
    const DECTM2 = 0xf7f2
    const DECCNT = 0xf7f4

    // ; F7F6-F805: workarea when executing numeric operators; intermediate
    // ; results are stored here; also used for parameter transfer when using
    // ; the USR functions; VALTYPE then contains the type, and the value is
    // ; stored like this:
    // ; typename  type  where
    // ; integer   2     F7F8-F7F9
    // ; string    3     F7F8-F7F9 (address descriptor)
    // ; single    4     F7F6-F7F9
    // ; double    8     F7F6-F7FD
    const DAC = 0xf7f6

    // ; F806-F856: workarea when executing numeric operators
    const HOLD8 = 0xf806
    const HOLD2 = 0xf836
    const HOLD = 0xf83e
    const ARG = 0xf847

    // ; F857-F85E: last calculated random double
    const RNDX = 0xf857

    // ; --------------------
    // ; filesystem work area
    // ; --------------------

    // ; F85F: # of filedescriptors reserved minus 1
    // ; this is also the maximum number of open files possible
    const MAXFIL = 0xf85f

    // ; F860-F861: start address of the file information table
    const FILTAB = 0xf860

    // ; F862-F863: start address of the first file-buffer
    const NULBUF = 0xf862

    // ; F864-F865: during file I/O the start address of the active file-buffer
    const PTRFIL = 0xf864

    // ; F866: flag indicating if the file that is being loaded have to be started
    // ; immediately; 0 = no, FF = yes
    const RUNFLG = 0xf866

    // ; note that RUNFLG and FILNAM overlap!

    // ; F866-F870: filename of last file that has been active;
    // ; first 8 chars are name, last 3 are extension
    const FILNAM = 0xf866

    // ; F871-F87B: second filename if needed, e.g. the NAME command
    const FILNM2 = 0xf871

    // ; F87C: switch indicating if currently a BASIC program is being loaded
    // ; 0 = no, 1 = yes
    const NLONLY = 0xf87c

    // ; F87D-F87E: workarea for BLOAD and BSAVE; when a part of normal memory
    // ; is written, it contains the end address of the written memory region
    // ; if video memory is written it contains $4BE5 + start address of the
    // ; written memory region ??
    const SAVEND = 0xf87d

    // ; F87F-F91E: storage area for the text of the function keys 10x16 bytes,
    // ; but strings need to be zero-terminated, soo maximum length of command is
    // ; 15 characters
    const FNKSTR = 0xf87f

    // ; ------------------------
    // ; screen routine work area
    // ; ------------------------

    // ; F91F-F921: start address of the standard ASCII pattern table
    // ; at every change towards a text mode it is copied in the pattern table
    // ; of the VDP
    // ;   F91F: slot indication (ini: 0)
    // ;   F920-F921: address (ini: 1BBF)
    // ; TODO: make CBIOS use this value instead of hardcoded value
    const CGPNT = 0xf91f

    // ; F922-F923: start address of the nametable in the VRAM
    const NAMBAS = 0xf922

    // ; F924-F925: start address of the pattern table in the VRAM
    const CGPBAS = 0xf924

    // ; F926-F927: start address of the sprite pattern table in the VRAM
    const PATBAS = 0xf926

    // ; F928-F929: start address of the sprite location table in the VRAM
    const ATRBAS = 0xf928

    // ; F92A-F92B: address in VRAM of the pattern of the current position
    // ; on screen
    const CLOC = 0xf92a

    // ; F92C: mask for CLOC selecting the right bits that correspond with
    // ; the current position
    const CMASK = 0xf92c

    // ; F92D-F930: work area for graphical calculations
    const MINDEL = 0xf92d
    const MAXDEL = 0xf92f

    // ; ----------------------------------------------
    // ; F931-F941: work area for calculation of CIRCLE
    // ; ----------------------------------------------

    // ; F931-F932: ratio of # of dots in the horizontal and vertical direction
    // ; if = $0100 then ASPCT1 and ASPCT2 are used
    // ; if < $0100 then it is the # of dots in one direction for each
    // ; $0100 # of dots in the other direction; the direction is indicated
    // ; by CSCLXY
    const ASPECT = 0xf931

    // ; F933:F934: ; distance, in # of dots from the center of the most
    // ; distant point of the circle
    const CENCNT = 0xf933

    // ; F935: switch indication if the start and/or end point need to be
    // ; connected to the center
    // ;  bit 7: connect end point; 1 = yes
    // ;  bit 0: connect start point; 1 = yes
    const CLINEF = 0xf935

    // ; F936-F937: used during calculation of CIRCLE
    const CNPNTS = 0xf936

    // ; F938: direction of drawing of circle:
    // ;  00 = from CSTCNT towards CENCNT
    // ;  FF = from CENCNT towards CSTCNT
    const CPLOTF = 0xf938

    // ; F939-F93A: used during calculation of CIRCLE
    const CPCNT = 0xf939

    // ; F93B-F93C: ; contains the total # of dots of the full circle,
    // ; even when only a part is drawn
    const CPCNT8 = 0xf93b

    // ; F93D-F93E: used during calculation of CIRCLE
    const CRCSUM = 0xf93d

    // ; F93F-F940: ; distance in dots from the center towards the closest
    // ; circle point
    const CSTCNT = 0xf93f

    // ; F941: switch indicating if the X or Y direction needs to be streched:
    // ; 0 = X, 1 = Y
    const CSCLXY = 0xf941

    // ; F942-F943: store of CLOC, also used for PAINT
    const CSAVEA = 0xf942

    // ; F944: storage of CMASK; also used for PAINT
    const CSAVEM = 0xf944

    // ; F945-F946: horizontal distance towards the center
    const CXOFF = 0xf945

    // ; F947-F948: vertical distance towards the center
    const CYOFF = 0xf947

    // ; -------------------------------------------
    // ; work area for executing the PAINT statement
    // ; -------------------------------------------

    // ; F949: leftmost position of protrusion towards the left
    const LOHMSK = 0xf949

    // ; F94A: new workdirection for protrusion towards the left
    const LOHDIR = 0xf94a

    // ;F94B-F94C: leftmost position of protrusion towards the left
    const LOHADR = 0xf94b

    // ; F94D: size of protrusion towards the left
    const LOHCNT = 0xf94d

    // ; F94F-F950: # of pixels that may be skipped
    const SKPCNT = 0xf94f

    // ; F951-F952: # of movements
    const MOVCNT = 0xf951

    // ; F953: current direction; $40 = \/, $C0 = /\, $00 = stop
    const PDIREC = 0xf953

    // ; F954: indicate if paint towards the left worked
    const LFPROG = 0xf954

    // ; F955: indicate of a paint towards the right worked
    const RTPROG = 0xf955

    // ; F956-F957: start address of a jumptable for subcommands
    // ; contained in a string variable, used for both PLAY and DRAW
    // ; where this systemvar points to either the PLAY or the DRAW
    // ; table
    const MCLTAB = 0xf956

    // ; F958: switch indication if MCLTAB is for PLAY or DRAW
    // ; $0 = DRAW, $FF = PLAY
    const MCLFLG = 0xf958

    // ; ------------------------------------------
    // ; work area for sound and queueing and RS232
    // ; ------------------------------------------

    // ; F959-F971: Variables for three music queues and one RS232 queue
    // ; F959: VOICAQ put position
    // ; F95A: VOICAQ get position
    // ; F95B: VOICAQ putback flag
    // ; F95C: VOICAQ size
    // ; F95D: VOICAQ address
    // ; F95F-F964: VOICBQ
    // ; F965-F96A: VOICCQ
    // ; F96B-F970: RS2IQ
    const QUETAB = 0xf959

    // ; Putback characters for queues. TODO: what purpose do these have exactly?
    const QUEBAK = 0xf971

    // ; Buffers for queues.
    const VOICAQ = 0xf975                // ; Voice A queue
    const VOICBQ = 0xf9f5                // ; Voice B queue
    const VOICCQ = 0xfa75                // ; Voice C queue
    const RS2IQ = 0xfaf5                 // ; RS232   queue

    // ; in MSX2 the content of RS2IQ is used differently:
    const DPPAGE = 0xfaf5                // ; Display page (SCR5+)
    const ACPAGE = 0xfaf6                // ; Active page (SCR5+)

    // ; FAF7: AV control port value storage
    const AVCSAV = 0xfaf7

    // ; FAF8: extended BASIC ROM slot address
    const EXBRSA = 0xfaf8

    // ; FAF9: character count for ROMA-KANA
    const CHRCNT = 0xfaf9

    // ; FAFA-FAFB: character save for ROMA-KANA
    const ROMA = 0xfafa

    // ; ROMA-KANA extension mode switch or VRAM size??
    const MODE = 0xfafc

    // ;Reserved       equ     $FAFD

    // ; FAFE-FAFF: x position for mouse or lightpen
    const XSAVE = 0xfafe

    // ; FB00-FB01: y position for mouse or lightpen
    const YSAVE = 0xfb00
    const LOGOPR = 0xfb02

    // ; FB21-FB28: Table which contains info for up to 4 disk ROMs, 2 bytes each:
    // ; - first byte: number of drives connected to this interface
    // ; - second byte: slot ID of the disk ROM
    const DRVINF = 0xfb21

    // ; end of MSX2 only usage of RS2IQ

    // ; --------------------------------
    // ; work area for the PLAY statement
    // ; --------------------------------

    // ; FB35: status about the parsing of a PLAY string
    // ;  bit 7: only one time parsed; 1 = yes
    // ;  bit 1-0: number of parsed strings (0-3)
    const PRSCNT = 0xfb35

    // ; FB36-FB37: storage of stack
    const SAVSP = 0xfb36

    // ; FB38: # of voice currently being parsed (0-2)
    const VOICEN = 0xfb38

    // ; FB39-FB3A: storage of volume of a muted voice
    const SAVVOL = 0xfb39

    // ; FB3B: size of string being parsed (also used by DRAW)
    const MCLLEN = 0xfb3b

    // ; FB3C-FB3D: address of string being parsed (also used by DRAW)
    const MCLPTR = 0xfb3c

    // ; FB3E: temporarely storage of active queue # (0-2)
    const QUEUEN = 0xfb3e

    // ; FB3F: flag indicating which queues are active
    // ; bit 2 = queue 2; 1 = active
    // ; bit 1 = queue 1; 1 = active
    // ; bit 0 = queue 0; 1 = active
    const MUSICF = 0xfb3f

    // ; FB40: count of the # of PLAY statements parsed, but not executed yet
    const PLYCNT = 0xfb40

    // ; FB41-FB65: Voice Control Block for voice A (queue 0)
    const VCBA = 0xfb41

    // ; FB66-FB8A: Voice Control Block for voice B (queue 1)
    const VCBB = 0xfb66

    // ; FB8B-FBAF: Voice Control Block for voice C (queue 2)
    const VCBC = 0xfb8b

    // ; each VCB has the following structure:
    // ; name                  offset  length  purpose
    const METREX = 0                     // ;     2       interrupt counter
    const VCXLEN = 2                     // ;     1       MCLLEN for voice
    const VCXPTR = 3                     // ;     2       MCLPTR for voice
    const VCXSTP = 5                     // ;     2       stack pointer
    const QLENGX = 7                     // ;     1       # bytes in queue
    const NTICSX = 8                     // ;     2       new counter ?
    const TONPRX = 10                    // ;     2       pitch
    const AMPLTX = 12                    // ;     1       amplitude
    const ENVPRX = 13                    // ;     2       envelope speed
    const OCTAVX = 15                    // ;     1       octave
    const NOTELX = 16                    // ;     1       tone length
    const TEMPOX = 17                    // ;     1       tempo
    const VOLUMX = 18                    // ;     1       volume
    const ENVLPX = 19                    // ;     1       envelope shape
    const MCLSTX = 33                    // ;             space for stack storage
    const MCLSEX = 36                    // ;             start of stack

    // ; the stack mentioned above is used to store bytevalues
    // ; that are readied to be put on the voice queue

    // ; -----------------------------------------------
    // ; settings for screen editor and interrupt system
    // ; -----------------------------------------------

    // ; FBB0: switch indicating if software reset is enabled
    // ; 0 = n, 1 = yes; can be used to reset BASIC by pressing
    // ; SHIFT-CODE-GRAPH; does not erase the existing program
    // ; (ini: 0)
    const ENSTOP = 0xfbb0

    // ; FBB1: switch indicating if the current BASIC program is in a ROM
    // ; 0 = no; 1 = yes
    const BASROM = 0xfbb1

    // ; FBB2-FBC9: table containing for each line if it continues on the
    // ; next line; 0 = yes, >0 = no
    const LINTTB = 0xfbb2

    // ; FBCA-FBCB storage of location of cursor for INLIN and QINLIN
    // ;  FBCA: CSRY , FBCB: CSRX
    const FSTPOS = 0xfbca

    // ; ASCII code of the character currently covered by the cursor
    // ; TODO: is the name CURSAV or CODSAV ?
    const CURSAV = 0xfbcc

    // ; FBCD: switch indicating which function keys are to be displayed
    // ; on the screen; 0 = F6-F10, 1 = F1-F5
    const FNKSWI = 0xfbcd

    // ; FBCE-FBD7: for each function key, a flag indicating if it has
    // ; interrupt facility enabled; 0 = disabled, 1 = enabled
    const FNKFLG = 0xfbce

    // ; FBD8: counter of # of interrupts that still have a pending ON .. GOSUB
    const ONGSBF = 0xfbd8

    // ; FBD9: flag indicating if a keyclick has already been generated, to avoid
    // ; keyclicks for a key that generates two ASCII codes
    // ; $00 = no click, $0F = click
    const CLIKFL = 0xfbd9

    // ; FBDA-FBE4: storage of keyboard matrix, used for detection key repetition
    const OLDKEY = 0xfbda

    // ; FBE5-FBEF: current state of the keyboard matrix
    const NEWKEY = 0xfbe5

    // ; keyboard buffer; each char entered via the keyboard ends up here
    const KEYBUF = 0xfbf0

    // ; LIMPNT: something about "key buffer pointer"
    const LIMPNT = 0xfc17

    // ; FC18-FC3F: work area for processing the last typed line
    const LINWRK = 0xfc18

    // ; FC40-FC47: storage for the patter of an ASCII character
    // ; used when writing an ASCII character in a graphical mode
    const PATWRK = 0xfc40

    // ; FC48-FC49: lowest address of the RAM memory; initialized at startup
    // ; and not changed normally
    const BOTTOM = 0xfc48

    // ; FC4A-FC4B: highest address of the RAM memory that is not reserved by
    // ; the OS; string area, filebuffers and stack are below this address
    // ; initialized at startup and not changed normally
    const HIMEM = 0xfc4a

    // ; FC4C-FC99: table for interrupt facilities of MSX BASIC
    // ; each 3 bytes are used like this:
    // ; byte 1 is a flag:
    // ;  bit 2: interrupt happened; 1 = yes
    // ;  bit 1: interrupt stop; 1 = yes
    // ;  bit 0: interrupt off; 1 = no
    // ; byte 2-3 is the adress of the line in BASIC where should be
    // ; jumped too
    // ; the offsets in the table are:
    // ;  offset  address interrupt
    // ;       0  FC4C    F1
    // ;       3  FC4F    F2
    // ;       6  FC52    F3
    // ;       9  FC55    F4
    // ;      12  FC58    F5
    // ;      15  FC5B    F6
    // ;      18  FC5E    F7
    // ;      21  FC61    F8
    // ;      24  FC64    F9
    // ;      27  FC67    F10
    // ;      30  FC6A    STOP
    // ;      33  FC6D    sprite collision
    // ;      36  FC70    SPACE (trigger 0)
    // ;      39  FC73    joystick 1 button 1 (trigger 1)
    // ;      39  FC76    joystick 2 button 1 (trigger 2)
    // ;      39  FC79    joystick 1 button 2 (trigger 3)
    // ;      39  FC7C    joystick 2 button 2 (trigger 4)
    // ;      39  FC7F    interval
    const TRPTBL = 0xfc4c

    // ; FC9A: usage unknown
    const RTYCNT = 0xfc9a

    // ; FC9B: STOP indication
    // ; 0 = nothing; 3 = CTRL+STOP, 4 = STOP
    const INTFLG = 0xfc9b

    // ; FC9C: last read Y-position of a touchpad
    const PADY = 0xfc9c

    // ; FC9D: last read X-position of a touchpad
    const PADX = 0xfc9d

    // ; FC9E-FC9F: software clock, updated at each VDP interrupt
    const JIFFY = 0xfc9e                 // ; timer counter

    // ; FCA0-FCA1: initial value of INTCNT, used when INTCNT
    // ; reaches 0; used for ON INTERVAL GOSUB
    const INTVAL = 0xfca0

    // ; FCA2-FCA3: interrupt counter; lowered at each VDP interrupt;
    // ; reset with value of INTVAL when it reaches zero; if interval
    // ; interrupt is needed, it is generated
    const INTCNT = 0xfca2

    // ; FCA4-FCA5: parameter used at tap input, given a value during
    // ; reading of a headerblock from tape
    const LOWLIM = 0xfca4
    const WINWID = 0xfca5

    // ; FCA6: flag indicating if the previous character written
    // ; to the screen was an extension character for graphical signs
    // ; (ASCII 1); 0 = no, 1 = yes
    const GRPHED = 0xfca6

    // ; FCA7 ESCCNT State of a state machine that handles the printing of escape
    // ; sequences. A subset of the VT52 escape sequences is supported.
    // ; values:
    // ; $00: not inside an escape sequence
    // ; $01: seen <ESC>x
    // ; $02: seen <ESC>y
    // ; $03: seen <ESC>Y<row>
    // ; $04: seen <ESC>Y
    // ; $FF: seen <ESC>
    const ESCCNT = 0xfca7

    // ; FCA8: switch indicating insert or overwrite mode
    // ; $00 = overwrite; $FF = insert
    // ; the value of INSFLG is changed each time the INS key is pressed
    const INSFLG = 0xfca8

    // ; FCA9: show cursor; 0 = no, 1 = yes
    // ; can be changed with escape sequences x5 and y5
    const CSRSW = 0xfca9

    // ; FCAA: shape of cursor; 0 = block; 1 = insert
    // ; pressing the INS key changes the value of CSTYLE
    // ; can be changed with escape sequences x4 and y4
    const CSTYLE = 0xfcaa

    // ; switch indicating if the CAPS-LOCK is on
    // ; $00 = off, $FF = on (unofficial: $80 = perma-on)
    const CAPST = 0xfcab

    // ; FCAC: dead key control in non-japanese MSX models
    // ; adds a mark on the next char pressed, if applicable
    // ;  0 = no dead key
    // ;  1 = dead key                => accent grave
    // ;  2 = SHIFT + dead key        => accent aigu
    // ;  3 = CODE + dead key         => accent circumflex
    // ;  4 = SHIFT + CODE + dead key => trema
    // ; in japanese models it controls the charset used
    const KANAST = 0xfcac

    // ; FCAD: only used in japanese MSX models; it defines
    // ; the used typeset (ini: $40)
    const KANAMD = 0xfcad

    // ; ----
    // ; misc
    // ; ----

    const FLBMEM = 0xfcae
    const SCRMOD = 0xfcaf
    const OLDSCR = 0xfcb0
    const CASPRV = 0xfcb1
    const BRDATR = 0xfcb2
    const GXPOS = 0xfcb3
    const GYPOS = 0xfcb5
    const GRPACX = 0xfcb7
    const GRPACY = 0xfcb9
    const DRWFLG = 0xfcbb
    const DRWANG = 0xfcbd
    const RUNBNF = 0xfcbe
    const SAVENT = 0xfcbf

    // ; ---------------------------
    // ; storage of slot information
    // ; ---------------------------

    // ; FCC1-FCC4: Information for each primary slot. The most significant bit is
    // ; set if the primary slot is found to be expanded.
    const EXPTBL = 0xfcc1

    // ; FCC5-FCC8: Duplicate the contents of the four possible secondary slot
    // ; registers.
    const SLTTBL = 0xfcc5

    // ; FCC9-FD08: Information for any extension ROMs found during the power-up
    // ; ROM search.
    // ; FCC9-FCCC: primary slot 0, secondary slot 0
    // ; FCCD-FCD0: primary slot 0, secondary slot 1
    // ; FCD1-FCD4: primary slot 0, secondary slot 2
    // ; FCD5-FCD8: primary slot 0, secondary slot 3
    // ; FCD9-FCE8: primary slot 1
    // ; FCE9-FCF8: primary slot 2
    // ; FCF9-FD08: primary slot 3
    // ; The information is stored as below.
    // ; bit 7 (set): BASIC program
    // ; bit 6 (set): device handler
    // ; bit 5 (set): statement handler
    const SLTATR = 0xfcc9
    const SLTWRK = 0xfd09

    // ; ------------------------------
    // ; storage of ROM-page parameters
    // ; ------------------------------

    const PROCNM = 0xfd89
    const DEVICE = 0xfd99

    // ; ------------
    // ; system hooks
    // ; ------------

    // ; system hooks are defined in hooks.asm

    // ; ------------------
    // ; storage of VDP8-23
    // ; ------------------

    // ; FFE7-FFF6: storage of VDP 8-23
    const RG8SAV = 0xffe7

    // ; ----------------------
    // ; extra slot information
    // ; ----------------------

    // ; FFF7: slot address of main-rom
    // ;?????:         equ     $FFF7

    // ; ------------------
    // ; storage of VDP25-27
    // ; ------------------

    // ; FFFA-FFFC: storage of VDP 25-27
    const RG25SAV = 0xfffa

    // ; ---------------------------
    // ; subslot switching addresses
    // ; ---------------------------

    // ; FFFF: subslot switching address
    // ; This is not actually a system variable, it is a hardware register:
    // ;   SSL_REGS (see hardware.asm).

    // ; -------
    // ; the end
    // ; -------

    // ; vim:ts=8:expandtab:filetype=z8a:syntax=z8a:
    // -- end: systemvars.asm

    // -- begin: hooks.asm

    // ; C-BIOS hook declarations
    // ;
    // ; Copyright (c) 2002-2003 BouKiCHi.  All rights reserved.
    // ; Copyright (c) 2003 Reikan.  All rights reserved.
    // ; Copyright (c) 2004 Maarten ter Huurne.  All rights reserved.
    // ; Copyright (c) 2004 Manuel Bilderbeek.  All rights reserved.
    // ; Copyright (c) 2004-2006 Joost Yervante Damad.  All rights reserved.
    // ;
    // ; 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.
    // ;
    // ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
    // ;

    // ; called at start of interrupt subroutine, before it has been checked if
    // ; the interrupt was from VDP; used by e.g. RS232
    const H_KEYI = 0xfd9a

    // ; called at start of interrupt subroutine, when it is clear that
    // ; the interrupt is from the VDP
    const H_TIMI = 0xfd9f

    // ; called at start of CHPUT(00A2)
    const H_CHPU = 0xfda4

    // ; called at start of the subroutine drawing the cursor
    const H_DSPC = 0xfda9

    // ; called at start of the subroutine that removes the cursor
    const H_ERAC = 0xfdae

    // ; called at start of DSPFNK(00CF)
    const H_DSPF = 0xfdb3

    // ; called at start of ERAFNK(00CC)
    const H_ERAF = 0xfdb8

    // ; called at start of TOTEXT(00D2)
    const H_TOTE = 0xfdbd

    // ; called at start of CHGET(009F)
    const H_CHGE = 0xfdc2

    // ; called at start of the subroutine that fills the pattern-table
    // ; can be used to override the default ASCII patterns
    const H_INIP = 0xfdc7

    // ; called at part of subroutine that decodes combined keystrokes
    // ; like with CTRL/CODE/GRAPH/SHIFT to an ASCII code; can be used
    // ; to override the working of the keyboard
    const H_KEYC = 0xfdcc

    // ; called at start of the subroutine that decodes single keystrokes;
    // ; can be used to override the working of the keyboard
    const H_KYEA = 0xfdd1

    // ; called at start of NMI interrupt subroutine
    const H_NMI = 0xfdd6

    // ; called at start of PINLIN(00AE)
    const H_PINL = 0xfddb

    // ; called at start of QINLIN(00B4)
    const H_QINL = 0xfde0

    // ; called at start of INLIN(00B1)
    const H_INLI = 0xfde5

    // ; BASIC interpreter hook
    const H_ONGO = 0xfdea

    // ; implementation hook for DSKO$
    const H_DSKO = 0xfdef

    // ; implementation hook for SET
    const H_SETS = 0xfdf4

    // ; implementation hook for NAME
    const H_NAME = 0xfdf9

    // ; implementation hook for KILL
    const H_KILL = 0xfdfe

    // ; implementation hook for IPL
    const H_IPL = 0xfe03

    // ; implementation hook for COPY
    const H_COPY = 0xfe08

    // ; implementation hook for CMD
    const H_CMD = 0xfe0d

    // ; implementation hook for DSKF
    const H_DSKF = 0xfe12

    // ; implementation hook for DSKI$
    const H_DSKI = 0xfe17

    // ; implementation hook for ATTR$
    const H_ATTR = 0xfe1c

    // ; implementation hook for LSET
    const H_LSET = 0xfe21

    // ; implementation hook for RSET
    const H_RSET = 0xfe26

    // ; implementation hook for FIELD
    const H_FIEL = 0xfe2b

    // ; implementation hook for MKI$
    const H_MKIS = 0xfe30

    // ; implementation hook for MKS$
    const H_MKSS = 0xfe35

    // ; implementation hook for MKD$
    const H_MKDS = 0xfe3a

    // ; implementation hook for CVI
    const H_CVI = 0xfe3f

    // ; implementation hook for CVS
    const H_CVS = 0xfe44

    // ; implementation hook for CVD
    const H_CVD = 0xfe49

    // ; called when looking up the value of PTRFIL(F864) for DISKBASIC
    const H_GETP = 0xfe4e

    // ; called when PTRFIL(F864) is being given a new value
    const H_SETF = 0xfe53

    // ; called when an OPEN statement was issued without a FOR-part
    // ; part of DISKBASIC
    const H_NOFO = 0xfe58

    // ; called for an operation for file-buffer 0, in DISKBASIC
    const H_NULO = 0xfe5d

    // ; called from DISKBASIC for a call with file-buffer not 0
    const H_NTFL = 0xfe62

    // ; called when doing a MERGE command for disks
    const H_MERG = 0xfe67

    // ; called when doing a SAVE commands for disks
    const H_SAVE = 0xfe6c

    // ; called when doing a BSAVE command for disks
    const H_BINS = 0xfe71

    // ; called when doing a BLOAD command for disks
    const H_BINL = 0xfe76

    // ; implementation hook for FILES
    const H_FILE = 0xfe7b

    // ;  DISKBASIC hook
    const H_DGET = 0xfe80

    // ;  DISKBASIC hook
    const H_FILO = 0xfe85

    // ;  DISKBASIC hook
    const H_INDS = 0xfe8a

    // ; DISKBASIC entry for selecting the previous disk station for disk IO
    const H_RSLF = 0xfe8f

    // ; DISKBASIC entry for remembering the current disk station
    const H_SAVD = 0xfe94

    // ; implementation hook for LOC
    const H_LOC = 0xfe99

    // ; implementation hook for LOF
    const G_LOF = 0xfe9e

    // ; called when doing EOF for a disk in DISKBASIC
    const H_EOF = 0xfea3

    // ; implementation hook for FPOS
    const H_FPOS = 0xfea8

    // ; DISKBASIC hook
    const H_BAKU = 0xfead

    // ; called when BASIC interpreter is decoding the device part of a filename
    const H_PARD = 0xfeb2

    // ; called when BASIC interpreter finds a file without a device part
    const H_NODE = 0xfeb7

    // ; DISKBASIC hook
    const H_POSD = 0xfebc

    // ; called when searching a device by name
    const H_DEVN = 0xfec1

    // ; BASIC interpreter hook
    const H_GEND = 0xfec6

    // ; Called when clearing variables during the preparation of a RUN statement.
    // ; Also used by the disk ROM to start boot sequence.
    const H_RUNC = 0xfecb

    // ; called when doing CLEAR
    const H_CLEA = 0xfed0

    // ; BASIC interpreter hook
    const H_LOPD = 0xfed5

    // ; BASIC interpreter hook; called at stack error
    const H_STKE = 0xfeda

    // ; called at the start of ISFLIO(014A)
    const H_ISFL = 0xfedf

    // ; called at the start of OUTDO(0018)
    const H_OUTD = 0xfee4

    // ; BASIC interpreter hook
    const H_CRDO = 0xfee9

    // ; BASIC interpreter hook
    const H_DSKC = 0xfeee

    // ; called at the end of a BASIC program
    const H_PRGE = 0xfef8

    // ; BASIC interpreter hook
    const H_ERRP = 0xfefd

    // ; BASIC interpreter hook
    const H_ERRF = 0xff02

    // ; BASIC interpreter hook
    const H_READ = 0xff07

    // ; BASIC interpreter hook
    const H_MAIN = 0xff0c

    // ; called when executing a BASIC statement in direct mode
    const H_DIRD = 0xff11

    // ; BASIC interpreter hook
    const H_FINI = 0xff16

    // ; BASIC interpreter hook
    const H_FINE = 0xff1b

    // ; called while encoding a just typed BASIC statement
    const H_CRUN = 0xff20

    // ; called while encoding a just typed BASIC statement
    const H_CRUS = 0xff25

    // ; called when a keyword has been found while encoding a just typed
    // ; BASIC statement
    const H_ISRE = 0xff2a

    // ; called when a function has been found while encoding a just typed
    // ; BASIC statement
    const H_NTFN = 0xff2f

    // ; called when a non-keyword has been found while encoding a just
    // ; typed BASIC statement
    const H_NOTR = 0xff34

    // ; BASIC interpreter hook
    const H_SNGF = 0xff39

    // ; BASIC interpreter hook
    const H_NEWS = 0xff3e

    // ; BASIC interpreter hook
    const H_GONE = 0xff43

    // ; called at start of CHRGTR(0010)
    const H_CHRG = 0xff48

    // ; BASIC interpreter hook
    const H_RETU = 0xff4d

    // ; BASIC interpreter hook
    const H_PRTF = 0xff52

    // ; BASIC interpreter hook
    const H_COMP = 0xff57

    // ; BASIC interpreter hook
    const H_FINP = 0xff5c

    // ; BASIC interpreter hook
    const H_TRMN = 0xff61

    // ; BASIC interpreter hook
    const H_FRME = 0xff66

    // ; BASIC interpreter hook
    const H_NTPL = 0xff6b

    // ; called when calculating the value of an expression in BASIC
    const H_EVAL = 0xff70

    // ; BASIC interpreter hook
    const H_OKNO = 0xff75

    // ; BASIC interpreter hook
    const H_FING = 0xff7a

    // ; called when setting a value to a substring with MID$
    const H_ISMI = 0xff7f

    // ; called when executing the WIDTH statement
    const H_WIDT = 0xff84

    // ; called when executing the LIST statement
    const H_LIST = 0xff89

    // ; BASIC interpreter hook
    const H_BUFL = 0xff8e

    // ; BASIC interpreter hook
    const H_FRQI = 0xff93

    // ; BASIC interpreter hook
    const H_SCNE = 0xff98

    // ; BASIC interpreter hook
    const H_FRET = 0xff9d

    // ; called when looking up a variable in BASIC
    const H_PTRG = 0xffa2

    // ; called from within PHYDIO(0144), to allow its implementation
    const H_PHYD = 0xffa7

    // ; called from within FORMAT(147), to allow its implementation
    const H_FORM = 0xffac

    // ; called form the error-handling routine of the BASIC interpreter
    const H_ERRO = 0xffb1

    // ; called at start of LPTOUT(00A5)
    const H_LPTO = 0xffb6

    // ; called at start of LPTSTT(00A8)
    const H_LPTS = 0xffbb

    // ; called when executing SCREEN
    const H_SCRE = 0xffc0

    // ; called when executing PLAY
    const H_PLAY = 0xffc5

    // ; allows for installation of expansion devices that contain extra OS subroutines
    const H_BEXT = 0xffca

    // ; vim:ts=8:expandtab:filetype=z8a:syntax=z8a:
    // -- end: hooks.asm

    // ;-----------------
    // ; A memory address for debug
    // ;-----------------

    const DISPADDR = 0xe010              // ; Memory for dump routine
    const LASTSTAC = 0xe000
    const SP_REGS = 0xe002
    const COMPILE_FONT = YES

    // ;---------------------
    // ; Jump table
    // ;---------------------

    // ; $0000 CHKRAM
    // org 0x00
    DI
    JP chkram

    // ; Pointer to font
    // ; $0004 CGTABL  Base address of the MSX character set in ROM
    org> 0x0004
    data word [B_Font]
    org> 0x0006

    // ; $0006 VDP.DR  Base port address for VDP data read
    data vdp_dr = byte [ /*unused*/
        VDP_DATA                         // ; VDP read port
    ]

    // ; $0007 VDP.WR  Base port address for VDP data write
    data vdp_dw = byte [ /*unused*/
        VDP_DATA                         // ; VDP write port
    ]

    // ; $0008 SYNCHR
    org> 0x0008
    JP synchr

    // ; $000C RDSLT   Read memory from an optional slot
    org> 0x000c
    JP rdslt

    // ; $0010 CHRGTR
    org> 0x0010
    JP chrgtr

    // ; $0014 WRSLT   Write memory to an optional slot
    org> 0x0014
    JP wrslt

    // ; $0018 OUTDO
    org> 0x0018
    JP outdo

    // ; $001C CALSLT   inter slot call routine
    org> 0x001c
    JP calslt

    // ; $0020 DCOMPR  Compare HL to DE
    org> 0x0020
    JP dcompr

    // ; $0024 ENASLT  Change slot
    org> 0x0024
    JP enaslt

    // ; $0028 GETYPR
    org> 0x0028
    JP getypr

    // ; $002B IDBYT1
    org> 0x002b

    data idbyt1 = byte [ /*unused*/
        // ; Basic ROM version
        // ; 7 6 5 4 3 2 1 0
        // ; | | | | +-+-+-+-- Character set
        // ; | | | |           0 = Japanese, 1 = International (ASCII), 2=Korean
        // ; | +-+-+---------- Date format
        // ; |                 0 = Y-M-D, 1 = M-D-Y, 2 = D-M-Y
        // ; +---------------- Default interrupt frequency
        // ;                   0 = 60Hz, 1 = 50Hz
        (LOCALE_CHSET + LOCALE_DATE + LOCALE_INT)
    ]

    // ; $002C IDBYT2
    data idbyt2 = byte [ /*unused*/
        // ; Basic ROM version
        // ; 7 6 5 4 3 2 1 0
        // ; | | | | +-+-+-+-- Keyboard type
        // ; | | | |           0 = Japanese, 1 = International (QWERTY)
        // ; | | | |           2 = French (AZERTY), 3 = UK, 4 = German (DIN)
        // ; +-+-+-+---------- Basic version
        // ;                   0 = Japanese, 1 = International
        (LOCALE_KBD + LOCALE_BASIC)
    ]

    // ; $002D Version ID
    romid: /*unused*/
    org> 0x002d
    data byte [
        // ; version ID
        // ; MSX version number
        // ;  0 = MSX 1
        // ;  1 = MSX 2
        // ;  2 = MSX 2+
        // ;  3 = MSX turbo R
        MODEL_MSX
    ]

    // ; Bit 0: if 1 then MSX-MIDI is present internally (MSX turbo R only)
    data byte [
        0

        // ; Reserved
        0
    ]

    // ; $0030 CALLF    Call interslot routine(RST30h version)
    org> 0x0030
    JP callf

    // ; $0038 KEYINT   Interrupt routines(RST38,VBlank,Timer...)
    org> 0x0038
    JP keyint

    // ; $003B INITIO  Initialize I/O
    org> 0x003b
    JP initio

    // ; $003E INIFNK
    org> 0x003e
    JP inifnk

    // ; $0041 DISSCR  Disable screen display
    org> 0x0041
    JP disscr

    // ; $0044 ENASCR  Enable screen display
    org> 0x0044
    JP enascr

    // ;---------------
    // ;VDP routines
    // ;---------------

    // ; $0047 WRTVDP
    org> 0x0047
    JP wrtvdp

    // ; $004A RDVRM
    org> 0x004a
    JP rdvrm

    // ; $004D WRTVRM
    org> 0x004d
    JP wrtvrm

    // ; $0050 SETRD
    org> 0x0050
    JP setrd

    // ; $0053 SETWRT  Set VRAM Write Address
    org> 0x0053
    JP setwrt

    // ; $0056 FILVRM  Fill VRAM
    org> 0x0056
    JP filvrm

    // ; $0059 LDIRMV  Copy VRAM to RAM
    org> 0x0059
    JP ldirmv

    // ; $005C LDIRVM  Copy RAM to VRAM
    org> 0x005c
    JP ldirvm

    // ; $005F CHGMOD Change VDP screen mode
    org> 0x005f
    JP chgmod

    // ; $0062 CHGCLR
    org> 0x0062
    JP chgclr

    // ; $0066 NMI     Non-maskable interrupt
    org> 0x0066
    JP nmi

    // ; $0069 CLRSPR  Clear sprites
    org> 0x0069
    JP clrspr

    // ; $006C INITXT  Initialize display to mode TEXT1    (SCREEN 0)
    org> 0x006c
    JP initxt

    // ; $006F INIT32  Initialize display to mode GRAPHIC1 (SCREEN 1)
    org> 0x006f
    JP init32

    // ; $0072 INITGRP Initialize display to mode GRAPHIC2 (SCREEN 2)
    org> 0x0072
    JP inigrp

    // ; $0075 INIMLT  Initialize display to mode MULTI    (SCREEN 3)
    org> 0x0075
    JP inimlt

    // ; $0078 SETTXT
    org> 0x0078
    JP settxt

    // ; $007B SETT32
    org> 0x007b
    JP sett32

    // ; $007E SETGRP
    org> 0x007e
    JP setgrp

    // ; $0081 SETMLT
    org> 0x0081
    JP setmlt

    // ; $0084 CALPAT
    org> 0x0084
    JP calpat

    // ; $0087 CALATR
    org> 0x0087
    JP calatr

    // ; $008A GSPSIZ
    org> 0x008a
    JP gspsiz

    // ; $008D GRPPRT
    org> 0x008d
    JP grpprt

    // ; $0090 GICINI  initialize sound IC
    org> 0x0090
    JP gicini

    // ; $0093 WRTPSG
    org> 0x0093
    JP wrtpsg

    // ; $0096 RDPSG
    org> 0x0096
    JP rdpsg

    // ; $0099 STRTMS
    org> 0x0099
    JP strtms

    // ; $009C CHSNS  .. check key buffer
    org> 0x009c
    JP chsns

    // ; $009F CHGET .. Get data from keyboard buffer
    org> 0x009f
    JP chget

    // ; $00A2 CHPUT .. Output charactor to display
    org> 0x00a2
    JP chput

    // ; $00A5 LPTOUT
    org> 0x00a5
    JP lptout

    // ; $00A8 LPTSTT
    org> 0x00a8
    JP lptstt

    // ; $00AB CNVCHR
    org> 0x00ab
    JP cnvchr

    // ; $00AE PINLIN
    org> 0x00ae
    JP pinlin

    // ; $00B1 INLIN
    org> 0x00b1
    JP inlin

    // ; $00B4 QINLIN
    org> 0x00b4
    JP qinlin

    // ; $00B7 BREAKX
    org> 0x00b7
    JP breakx

    // ; $00BA ISCNTC
    org> 0x00ba
    JP iscntc

    // ; $00BD CKCNTC
    org> 0x00bd
    JP ckcntc

    // ; $00C0 BEEP
    org> 0x00c0
    JP beep

    // ; $00C3 CLS
    org> 0x00c3
    JP cls_z

    // ; $00C6 POSIT
    org> 0x00c6
    JP posit

    // ; $00C9 FNKSB
    org> 0x00c9
    JP fnksb

    // ; $00CC ERAFNK
    org> 0x00cc
    JP erafnk

    // ; $00CF DSPFNK
    org> 0x00cf
    JP dspfnk

    // ; $00D2 TOTEXT
    org> 0x00d2
    JP totext

    // ; $00D5 GTSTCK .. Get joystick infomation
    org> 0x00d5
    JP gtstck

    // ; $00D8 GTTRIG .. Get trigger infomation
    org> 0x00d8
    JP gttrig

    // ; $00DB GTPAD
    org> 0x00db
    JP gtpad

    // ; $00DE GTPDL
    org> 0x00de
    JP gtpdl

    // ; $00E1 TAPION
    org> 0x00e1
    JP tapion

    // ; $00E4 TAPIN
    org> 0x00e4
    JP tapin

    // ; $00E7 TAPIOF
    org> 0x00e7
    JP tapiof

    // ; $00EA TAPOON
    org> 0x00ea
    JP tapoon

    // ; $00ED TAPOUT
    org> 0x00ed
    JP tapout

    // ; $00F0 TAPOOF
    org> 0x00f0
    JP tapoof

    // ; $00F3 STMOTR
    org> 0x00f3
    JP stmotr

    // ; $00F6 LFTQ
    org> 0x00f6
    JP lftq

    // ; $00F9 PUTQ
    org> 0x00f9
    JP putq

    // ; $00FC RIGHTC
    org> 0x00fc
    JP rightc

    // ; $00FF LEFTC
    org> 0x00ff
    JP leftc

    // ; $0102 UPC
    org> 0x0102
    JP upc

    // ; $0105 TUPC
    org> 0x0105
    JP tupc

    // ; $0108 DOWNC
    org> 0x0108
    JP downc

    // ; $010B TDOWNC
    org> 0x010b
    JP tdownc

    // ; $010E SCALXY
    org> 0x010e
    JP scalxy

    // ; $0111 MAPXY
    org> 0x0111
    JP mapxy

    // ; $0114 FETCHC
    org> 0x0114
    JP fetchc

    // ; $0117 STOREC
    org> 0x0117
    JP storec

    // ; $011A SETATR
    org> 0x011a
    JP setatr

    // ; $011D READC
    org> 0x011d
    JP readc

    // ; $0120 SETC
    org> 0x0120
    JP setc

    // ; $0123 NSETCX
    org> 0x0123
    JP nsetcx

    // ; $0126 GTASPC
    org> 0x0126
    JP gtaspc

    // ; $0129 PNTINI
    org> 0x0129
    JP pntini

    // ; $012C SCANR
    org> 0x012c
    JP scanr

    // ; $012F SCANL
    org> 0x012f
    JP scanl

    // ; $0132 CHGCAP
    org> 0x0132
    JP chgcap

    // ; $0135 CHGSND
    org> 0x0135
    JP chgsnd

    // ; $0138 RSLREG  Read infomation of primary slot
    org> 0x0138
    JP rslreg

    // ; $013B WSLREG  Write infomation to primary slot
    org> 0x013b
    JP wslreg

    // ; $013E RDVDP   Read VDP status
    org> 0x013e
    JP rdvdp

    // ; $0141 SNSMAT  Get key matrix
    org> 0x0141
    JP snsmat

    // ; $0144 PHYDIO
    org> 0x0144
    JP phydio

    // ; $0147 FORMAT
    org> 0x0147
    JP format

    // ; $014A ISFLIO
    org> 0x014a
    JP isflio

    // ; $014D OUTDLP
    org> 0x014d
    JP outdlp

    // ; $0150 GETVCP
    org> 0x0150
    JP getvcp

    // ; $0153 GETVC2
    org> 0x0153
    JP getvc2

    // ; $0156 KILBUF  Clear keyboard buffer
    org> 0x0156
    JP kilbuf

    // ; $0159 CALBAS  Call BASIC interpreter
    org> 0x0159
    JP calbas

    // ; fake EXTROM call, fixes Nemesis 3 reset bug
    org> 0x015f
    RET

    // ; ---------------------
    // ; MSX TurboR BIOS calls
    // ; ---------------------

    // ; $0180 CHGCPU

    // ; $0183 GETCPU

    // ; $0186 PCMPLY

    // ; $0189 PCMREC

    // ; -------------------

    org> 0x0200

    // -- begin: util.asm

    // ; C-BIOS utility routines
    // ;
    // ; Copyright (c) 2004 Maarten ter Huurne.  All rights reserved.
    // ;
    // ; 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.
    // ;
    // ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
    // ;

    // ;----------------------------------
    // ; Generic routine that implements a jump table.
    // ; Input:   HL = address of jump table
    // ;          A  = index in table
    // ; Changes: F, HL, BC (these are changed *before* jumping)
    proc jump_table() {
        C <- A
        B <- 0
        HL + BC + BC
        JP table_jump
    }

    // ;----------------------------------
    // ; Generic routine that implements a search & jump table
    // ; Entries in the table consist of (character, jump address) pairs.
    // ; If the entry is not found, the routine will just RETurn.
    // ;
    // ; Input:   HL = address of search table
    // ;          A  = item to search for
    // ;          B  = number of entries in the table
    // ; Changes: F, BC, HL
    proc search_table() {
        A -? [HL]
        HL ++
        JR Z? table_jump

        HL ++ ++
        DJNZ search_table
        return
    }

    proc table_jump() {
        C <- [HL]
        HL ++
        H <- [HL]
        L <- C
        JP [HL]
    }

    // ; vim:ts=8:expandtab:filetype=z8a:syntax=z8a:
    // -- end: util.asm

    // ;include "slot.asm"

    // -- begin: video.asm

    // ; C-BIOS video routines
    // ;
    // ; Copyright (c) 2002-2005 BouKiCHi.  All rights reserved.
    // ; Copyright (c) 2003 Reikan.  All rights reserved.
    // ; Copyright (c) 2004-2006 Maarten ter Huurne.  All rights reserved.
    // ; Copyright (c) 2004-2005 Albert Beevendorp.  All rights reserved.
    // ; Copyright (c) 2004 Manuel Bilderbeek.  All rights reserved.
    // ; Copyright (c) 2004 Joost Yervante Damad.  All rights reserved.
    // ; Copyright (c) 2004-2005 Jussi Pitkänen.  All rights reserved.
    // ;
    // ; 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.
    // ;
    // ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
    // ;

    // ;--------------------------------
    // ; $0041 DISSCR
    // ; Function : inhibits the screen display
    // ; Registers: AF, BC
    proc disscr() {
        @[RG1SAV] & 0xbf -> B
        C <- 1
        wrtvdp()
        return
    }

    // ;--------------------------------
    // ; $0044 ENASCR
    // ; Function : displays the screen
    // ; Registers: AF, BC
    proc enascr() {
        @[RG1SAV] | 0x40 -> B
        C <- 1
        wrtvdp()
        return
    }

    // ;--------------------------------
    // ; 0047$ WRTVDP
    // ; Function : write data in the VDP-register
    // ; Input    : B  - data to write
    // ;            C  - number of the register
    // ; Output   : RG0SAV(F3DF)-RG7SAV(F3E6)
    // ; Registers: AF, BC
    proc wrtvdp() {
        DI

        C -reset 7                       // ; fixes High Way Star
        @B -out VDP_ADDR
        @C | 0x80 -out VDP_ADDR
        EI

        HL -push <- RG0SAV

    wrtvdp_sav: /*local*/
        A <- B
        B <- 0
        HL + BC
        A -> [HL]

    wrtvdp_nosav: /*local*/
        HL -pop
        return
    }

    // ;--------------------------------
    // ; $004A RDVRM
    // ; Function : Reads the content of VRAM
    // ; Input    : HL - address read
    // ; Output   : A  - value which was read
    // ; Registers: AF
    proc rdvrm() {
        setrd()

        // ; wait (at least) 29 t-states between VRAM accesses
        A | 0 -in VDP_DATA
        return
    }

    // ;--------------------------------
    // ; $004D WRTVRM
    // ; Function : Writes data in VRAM
    // ; Input    : HL - address write
    // ;            A  - value write
    // ; Registers: AF
    proc wrtvrm() {
        AF -push
        setwrt()

        AF -pop
        A -out VDP_DATA
        return
    }

    // ;--------------------------------
    // ; $0050 SETRD
    // ; Function : Enable VDP to read
    // ; Input    : HL - for VRAM-address
    // ; Registers: AF
    proc setrd() {
        DI

        @L -out VDP_ADDR
        @H & 0x3f -out VDP_ADDR
        EI
        return
    }

    // ;--------------------------------
    // ; $0053 SETWRT
    // ; Function : Enable VDP to write
    // ; Input    : HL - Address
    // ; Registers: AF
    proc setwrt() {
        DI

        @L -out VDP_ADDR
        @H & 0x3f | 0x40 -out VDP_ADDR
        EI
        return
    }

    // ;--------------------------------
    // ; $0056 FILVRM
    // ; Function : fill VRAM with value
    // ; Input    : A  - data byte
    // ;            BC - length of the area to be written
    // ;            HL - start address:
    // ;                 * SCREEN 0..4 -> 14-bit address
    // ;                 * SCREEN 5+ -> 17-bit address (uses ACPAGE)
    // ;                 Using 14-bit address for SCREEN4 doesn't really make sense,
    // ;                 but that's what we have to follow to be compatible.
    // ; Registers: AF, BC
    proc filvrm() {
        AF -push
        setwrt()

        BC --
        C ++
        A <- B
        B <- C
        C@A ++
        AF -pop

    // ; Note: Interrupts should be enabled here.
    // ;       Penguin Adventure can hang on boot if the interrupt
    // ;       comes just after our RET, which is certain if the
    // ;       memory block written is large enough.
    filvrm_lp: /*local*/
        A -out VDP_DATA

        // ; wait (at least) 29 t-states between VRAM accesses
        B --
        JR NZ? filvrm_lp

        C --
        JR NZ? filvrm_lp
        return
    }

    // ;--------------------------------
    // ; $0059 LDIRMV
    // ; Function : Block transfer from VRAM to memory
    // ; Input    : BC - blocklength
    // ;            DE - Start address of memory
    // ;            HL - Start address of VRAM
    // ; Registers: AF BC DE
    // ; Note     : the function doesn't destroy HL
    // ; Note     : the routine doesn't change IM
    proc ldirmv() {
        setrd()

        HL -push
        DE <-> HL
        BC --
        C ++
        A <- B
        B <- C
        A ++
        C <- VDP_DATA

    ldirmv_lp: /*local*/
        // ; wait (at least) 29 t-states between VRAM accesses
        INI
        JP NZ? ldirmv_lp

        A --
        JR NZ? ldirmv_lp

        HL -pop
        return
    }

    // ;--------------------------------
    // ; $005C LDIRVM
    // ; Function : Block transfer from memory to VRAM
    // ; Input    : BC - blocklength
    // ;            DE - Start address of VRAM
    // ;            HL - Start address of memory
    // ; Note     : the routine doesn't change IM
    // ; Registers: All
    proc ldirvm() {
        DE <-> HL
        setwrt()

        DE <-> HL
        BC --
        C ++
        A <- B
        B <- C
        A ++
        C <- VDP_DATA

    ldirvm_lp: /*local*/
        // ; wait (at least) 29 t-states between VRAM accesses
        OUTI
        JP NZ? ldirvm_lp

        A --
        JR NZ? ldirvm_lp

        // ; Note: Without this, Quinpl shows glitches.
        // ; TODO: Investigate why.
        DE <-> HL
        return
    }

    // ;----------------------------------
    // ; $005F CHGMOD  Changes screen mode
    // ; Function : Switches to given screenmode
    // ; Input    : A  - screen mode
    // ; Registers: All
    proc chgmod() {
        // ; Guard against non-existing screen mode.
        A -? 4
        return-if NC?

        // ; Redirect to initialisation routine.
        HL <- chgmod_tbl
        JP jump_table

        data chgmod_tbl = word [ /*local*/
            initxt                       // ; SCREEN0
            init32                       // ; SCREEN1
            inigrp                       // ; SCREEN2
            inimlt                       // ; SCREEN3
        ]
    }

    // ; TODO: Now that we rewrite most regs at the end of CHGMOD,
    // ;       the ini* routines can just update RG?SAV instead of calling wrtvdp.
    proc chgmod_finish() {
        // ; Generic state resets.

        // ; Write new values from system RAM to the VDP.
        DI

        // ; Write R#0 - R#7.
        HL <- RG0SAV
        BC <- (8 * 0x0100 + VDP_ADDR)
        D <- 0x80

    chgmod_finish_lp: /*local*/
        OUTI

        A <- B
        D -out C ++
        A | A
        JR NZ? chgmod_finish_lp
        EI
        JP enascr
    }

    // ;--------------------------------
    // ; $0062 CHGCLR
    // ; Function : Changes the screencolors
    // ; Input    : Foregroundcolor in FORCLR
    // ;            Backgroundcolor in BAKCLR
    // ;            Bordercolor in BDRCLR
    // ; Registers: All
    proc chgclr() {
        @[SCRMOD] -? 8
        JR Z? chgclr_sc8

        A --
        AF -push
        @[FORCLR] <* 4 & 0xf0 -> L
        @[BDRCLR] | L -> B
        C <- 7
        wrtvdp()

        AF -pop
        return-if NZ?

        // ; SCREEN1
        @[FORCLR] <* 4 & 0xf0
        HL <- BAKCLR
        A | [HL]
        HL <- [T32COL]
        BC <- 0x20
        AF -push
        setwrt()

    cclr_lp: /*local*/
        AF -pop
        A -out VDP_DATA
        AF -push
        BC --
        @B | C
        JR NZ? cclr_lp

        AF -pop
        return

    chgclr_sc8: /*local*/
        // ; SCREEN8
        @[BDRCLR] -> B
        C <- 7
        JP wrtvdp
    }

    // ;--------------------------------
    // ; $0069 CLRSPR
    // ; Function : Initialises all sprites
    // ; Input    : SCRMOD
    // ; Registers: All
    proc clrspr() {
        // ; Check screen mode.
        @[SCRMOD] | A
        return-if Z?                     // ; no sprites in SCREEN0

        // ; Clear sprite attribute table.
        CALL clrspr_attr

        // ; Clear sprite colour table.
        @[SCRMOD] -? 4                   // ; sprite mode 1?
        JR C? clrspr_col_skip

        HL <- [ATRBAS]
        H -- --                          // ; HL = (ATRBAS) - $200
        BC <- (32 * 16)
        @[FORCLR] & 0x0f
        filvrm()

    clrspr_col_skip: /*local*/
        // ; Clear sprite pattern generator table.
        HL <- [PATBAS]
        BC <- (256 * 8)
        A ^ A
        filvrm()
        return

    // ;--------------------------------
    // ; Clear sprite attribute table.
    clrspr_attr: /*local*/
        @[SCRMOD] -? 4
        JR C? clrspr_attr_spritemode1

    // ; Note: This label is called directly by external routines.
    clrspr_attr_spritemode2: /*local*/
        E <- 217                         // ; Y coordinate
        JR clrspr_attr_spritemode_start
    }

    // ; Note: This label is called directly by external routines.
    proc clrspr_attr_spritemode1() {
        E <- 209                         // ; Y coordinate
        fallthrough
    }

    proc clrspr_attr_spritemode_start() {
        HL <- [ATRBAS]
        setwrt()

        @[FORCLR] -> D
        BC <- 0x2000                     // ; B = 32 = counter, C = pattern index
        DI

    clrspr_attr_lp: /*local*/
        @E -out VDP_DATA                 // ; Y coordinate
        A <- 0
        NOP                              // ; wait (at least) 29 t-states between VRAM accesses
        NOP                              // ; only 2 nops, as ld a,0 is slow

        A -out VDP_DATA                  // ; X coordinate
          <- C
        NOP                              // ; wait (at least) 29 t-states between VRAM accesses
        NOP
        NOP

        A -out VDP_DATA                  // ; pattern number
        C ++
        gspsiz()
        JR NC? clrspr_attr_8

        C ++ ++ ++

    clrspr_attr_8: /*local*/
        @D -out VDP_DATA                 // ; color
        DJNZ clrspr_attr_lp
        EI
        return
    }

    // ;--------------------------------
    // ; $006C INITXT
    // ; Function : Switch to SCREEN 0
    // ; Input    : TXTNAM, TXTCGP
    // ; Output   : NAMBAS, CGPBAS, LINLEN, SCRMOD, OLDSCR
    // ; Registers: All
    proc initxt() {
        // ; Disable video output.
        disscr()

        // ; New screen mode.
        @0x00 -> [SCRMOD] -> [OLDSCR]

        // ; Line length.
        @[LINL40] -> [LINLEN]

        // ; Cursor position: top-left.
        @1 -> [CSRY] -> [CSRX]

        // ; Table base addresses.
        HL <- [TXTNAM]                   // ; name table
           -> [NAMBAS]
        HL <- [TXTCGP]                   // ; pattern table
           -> [CGPBAS]
        HL <- [TXTATR]                   // ; sprite attribute table (unused)
           -> [ATRBAS]
        HL <- [TXTPAT]                   // ; sprite pattern table (unused)
           -> [PATBAS]

        // ; Update VDP regs and VRAM.
        chgclr()
        settxt()
        init_font()
        cls_screen0()
        JP chgmod_finish
    }

    // ;--------------------------------
    // ; $006F INIT32
    // ; Function : Switches to SCREEN 1 (text screen with 32*24 characters)
    // ; Input    : T32NAM, T32CGP, T32COL, T32ATR, T32PAT
    // ; Output   : NAMBAS, CGPBAS, LINLEN, SCRMOD, OLDSCR
    // ; Registers: All
    proc init32() {
        // ; Disable video output.
        disscr()

        A <- 0x01                        // ; SCREEN1
          -> [SCRMOD] -> [OLDSCR]
        @1 -> [CSRY] -> [CSRX]
        chgclr()

        HL@[T32NAM] -> [NAMBAS]
        HL@[T32CGP] -> [CGPBAS]
        HL@[T32PAT] -> [PATBAS]
        HL@[T32ATR] -> [ATRBAS]
        init_font()

        @[LINL32] -> [LINLEN]
        sett32()
        clrspr_attr_spritemode1()
        cls_screen1()
        JP chgmod_finish
    }

    // ;--------------------------------
    // ; $0072 INIGRP
    // ; Function : Switches to SCREEN 2 (high resolution screen with 256*192 pixels)
    // ; Input    : GRPNAM, GRPCGP, GRPCOL, GRPATR, GRPPAT
    // ; Output   : NAMBAS-ATRBAS, SCRMOD
    // ; Registers: All
    proc inigrp() {
        // ; Disable video output.
        disscr()

        @0x02 -> [SCRMOD]
        chgclr()

        HL@[GRPNAM] -> [NAMBAS]
        setwrt()

        B <- 3
        A ^ A
        DI

    inigrp_lp: /*local*/
        A -out VDP_DATA ++
        JR NZ? inigrp_lp
        DJNZ inigrp_lp
        EI

        HL@[GRPCGP] -> [CGPBAS]
        HL@[GRPATR] -> [ATRBAS]
        HL@[GRPPAT] -> [PATBAS]
        setgrp()
        clrspr_attr_spritemode1()
        cls_screen2()
        JP chgmod_finish
    }

    // ;------------------------------
    // ; $0075 INIMLT
    // ; Function : Switches to SCREEN 3 (multi-color screen 64*48 pixels)
    // ; Input    : MLTNAM, MLTCGP, MLTCOL, MLTATR, MLTPAT
    // ; Output   : NAMBAS-ATRBAS, SCRMOD
    // ; Registers: All
    proc inimlt() {
        // ; Disable video output.
        disscr()

        @0x03 -> [SCRMOD]
        chgclr()

        HL@[MLTNAM] -> [NAMBAS]
        setwrt()

        A ^ A
        C <- 6
        DI

    inimlt_loop1: /*local*/
        AF -push
        E <- 4

    inimlt_loop2: /*local*/
        AF -push
        B <- 32

    inimlt_loop3: /*local*/
        A -out VDP_DATA ++
        DJNZ inimlt_loop3

        AF -pop
        E --
        JR NZ? inimlt_loop2

        AF -pop
        A + 32
        C --
        JR NZ? inimlt_loop1
        EI

        HL@[MLTCGP] -> [CGPBAS]
        HL@[MLTATR] -> [ATRBAS]
        HL@[MLTPAT] -> [PATBAS]
        setmlt()
        clrspr_attr_spritemode1()
        cls_screen3()
        JP chgmod_finish
    }

    // ;------------------------------
    // ; $0078 SETTXT
    // ; Function : Switches to VDP in SCREEN 0 mode
    // ; Input    : TXTNAM, TXTCGP
    // ; Registers: All
    proc settxt() {
        @[RG0SAV] & 0xf1                 // ; MASK 11110001
            -> B
        C <- 0
        wrtvdp()                         // ; write VDP R#0

        @[RG1SAV] & 0xe7                 // ; MASK 11100111
            | 0x10 -> B
        C ++
        wrtvdp()                         // ; write VDP R#1

        // ; Set the VDP base address registers. This works because
        // ; TXTNAM, TXTCOL and TXTCGP are in same order as the VDP
        // ; base address registers.
        DE <- TXTNAM
        C <- 2
        A ^ A
        set_base_address()

        DE ++                            // ; Skip TXTCOL.
           ++
        C ++
        A ^ A
        set_base_address()
        return

        // ; Switches VDP to TEXT2 mode (SCREEN 0, WIDTH 80).
    }

    // ;------------------------------
    // ; $007B SETT32
    // ; Function : Switches VDP to SCREEN 1 mode
    // ; Input    : T32NAM, T32COL, T32CGP, T32ATR, T32PAT
    // ; Registers: All
    proc sett32() {
        @[RG0SAV] & 0xf1                 // ; MASK 11110001
            -> B
        C <- 0
        wrtvdp()                         // ; write VDP R#0

        @[RG1SAV] & 0xe7                 // ; MASK 11100111
            -> B
        C ++
        wrtvdp()                         // ; write VDP R#1

        // ; Set the base address registers. This works because T32NAM,
        // ; T32COL, T32CGP, T32ATR and T32PAT are in same order as the
        // ; VDP base address registers.
        DE <- T32NAM
        C <- 2
        A ^ A
        set_base_address()

        A ^ A
        set_base_address()

        A ^ A
        set_base_address()

        A ^ A
        set_base_address()

        A ^ A
        set_base_address()
        return
    }

    // ;------------------------------
    // ; $007E SETGRP
    // ; Function : Switches VDP to SCREEN 2 mode
    // ; Input:     GRPNAM, GRPCOL, GRPCGP, GRPATR, GRPPAT
    // ; Registers: All
    proc setgrp() {
        @[RG0SAV] & 0xf1                 // ; MASK 11110001
            | 0x02                       // ; M3 = 1
            -> B
        C <- 0
        wrtvdp()                         // ; write VDP R#0

        @[RG1SAV] & 0xe7                 // ; MASK 11100111
            -> B
        C ++
        wrtvdp()                         // ; write VDP R#1

        // ; Set the base address registers. This works because GRPNAM,
        // ; GRPCOL, GRPCGP, GRPATR and GRPPAT are in same order as the
        // ; VDP base address registers.
        DE <- GRPNAM
        C <- 2
        A ^ A
        set_base_address()

        A <- 0x7f
        set_base_address()

        A <- 0x03
        set_base_address()

        A ^ A
        set_base_address()

        A ^ A
        set_base_address()
        return
    }

    // ;------------------------------
    // ; $0081 SETMLT
    // ; Function : Switches VDP to SCREEN 3 mode
    // ; Input    : MLTNAM, MLTCGP, MLTCOL, MLTATR, MLTPAT
    // ; Registers: All
    proc setmlt() {
        @[RG0SAV] & 0xf1 -> B
        C <- 0
        wrtvdp()

        @[RG1SAV] & 0xe7 | 0x08          // ; M2 = 1
            -> B
        C ++
        wrtvdp()

        // ; Set the base address registers. This works because MLTNAM,
        // ; MLTCOL, MLTCGP, MLTATR and MLTPAT are in same order as the
        // ; VDP base address registers.
        DE <- MLTNAM
        C <- 2
        A ^ A
        set_base_address()

        A ^ A
        set_base_address()               // ; TODO: Should we ignore MLTCOL?

        A ^ A
        set_base_address()

        A ^ A
        set_base_address()

        A ^ A
        set_base_address()
        return
    }

    // ;------------------------------
    // ; Get an address from a base address table, convert it into a register value,
    // ; and set the corresponding VDP base address register.
    // ; Input:     DE = pointer to a base address table
    // ;             C = VDP base address register
    // ;             A = OR-mask over the converted address
    // ; Output:    DE = DE + 2
    // ;             C =  C + 1
    // ; Changes:   AF, B, HL
    proc set_base_address() {
        DE -push
        AF -push

        // ; Get the shift value.
        HL <- set_base_address_table
        B <- 0
        HL + BC
        B <- [HL]

        // ; Get the address from (HL) to HL.
        DE <-> HL
        A <- [HL]
        HL ++
        H <- [HL]
        L <- A

    // ; Shift it to left in register A. After this A contains the
    // ; converted address.
    set_base_address_loop: /*local*/
        HL + HL
        A +$ A
        DJNZ set_base_address_loop

        B <- A

        // ; Set the base address register.
        AF -pop
        A | B -> B
        wrtvdp()

        // ; Increase pointer and register number.
        DE -pop ++ ++
        C ++
        return

        data set_base_address_table = byte [ /*local*/
            0x00 0x00 0x06 0x0a 0x05 0x09 0x05
        ]
    }

    // ;------------------------------
    // ; $0084 CALPAT
    // ; Returns the address of a sprite pattern in the sprite pattern table.
    // ; Input:     A  = pattern number
    // ; Output:    HL = address
    // ; Changes:   AF, DE, HL
    proc calpat() {
        H <- 0
        L <- A
        HL + HL + HL + HL
        gspsiz()
        JR NC? calpat_8

        HL + HL + HL

    calpat_8: /*local*/
        DE <- [PATBAS]
        HL + DE
        return
    }

    // ;------------------------------
    // ; $0087 CALATR
    // ; Returns the address of a sprite in the sprite attribute table.
    // ; Input:     A  = sprite number
    // ; Output:    HL = address
    // ; Changes:   AF, DE, HL
    proc calatr() {
        A + A + A
        HL <- [ATRBAS]
        D <- 0
        E <- A
        HL + DE
        return
    }

    // ;------------------------------
    // ; $008A GSPSIZ
    // ; Returns the current sprite-size in bytes.
    // ; Output:    A  = sprite-size in bytes
    // ;            CF = set when size is 16x16, otherwise reset
    // ; Changes:   AF
    proc gspsiz() {
        @[RG1SAV] >* 2
        A <- 8
        return-if NC?

        A <- 32
        return
    }

    // ;------------------------------
    // ; $008D GRPPRT
    // ; Function:  Places a character on graphic screen
    // ; Input:     A  - Character
    // ;            GRPACX , GRPACY : X, Y coordinate
    // ;            FORCLR
    // ; Input (SCREEN 5 and above) :
    // ;            LOGOPR for logical operator
    // ; NOTE : the function doesn't support without SCREEN 2
    // ;        and also slower yet.
    // ; Register : AF ???
    proc grpprt() {
        AF -push

        // ; Printable character or control character?
        A -? 0x20
        JR C? grpprt_control

        // ; Different implementation depending on screen mode.
        @[SCRMOD] -? 2
        JR Z? grpprt_sc2

        A -? 5
        JR NC? grpprt_sc5                // ; SCRMOD >= 5

    grpprt_end: /*local*/
        AF -pop
        return

    grpprt_control: /*local*/
        // ; Ignore everything except carriage return ($0D).
        A -? 0x0d
        JR NZ? grpprt_end

        AF -pop

        // ; Handle carriage return.
        HL -push
        BC -push
        HL <- [GRPACY]
        BC <- 0x08
        HL + BC -> [GRPACY]
        HL@0x00 -> [GRPACX]
        BC -pop
        HL -pop
        return

    grpprt_sc5: /*local*/
        AF -pop

        // ; TODO: should these routines be merged?
        return

    grpprt_sc2: /*local*/
        AF -pop
        HL -push
        DE -push
        BC -push
        AF -push
        getpat()

        DE <- [GRPACY]
        BC <- [GRPACX]
        mapxy()

        @[FORCLR] -> [ATRBYT]
        HL <- [CLOC]
        BC <- [GRPCGP]
        HL + BC
        DE <- PATWRK
        @[GRPACY] & 0x07
        B <- 0x00
        C <- A
        HL + BC
        CALL grpprt_chr_x

        BC <- 0xf0
        HL + BC
        @[GRPACY] -not & 0x07 -> C
        CALL grpprt_chr_x

        HL <- [GRPACX]
        BC <- 0x08
        HL + BC -> [GRPACX]
        AF -pop
        BC -pop
        DE -pop
        HL -pop
        return

    grpprt_chr_x: /*local*/
        @[GRPACX] & 0x07
        AF -push
        BC -push
        DE -push
        HL -push
        CALL grpprt_chr                  // ; half left

        @[GRPACX] & 0x07
        JR Z? grpprt_skip_hr

        @[CMASK] -not -> [CMASK]
        HL -pop
        BC <- 0x08
        HL + BC
        DE -pop
        BC -pop
        AF -pop
        CALL grpprt_chr                  // ; half right

        @[CMASK] -not -> [CMASK]
        return

    grpprt_skip_hr: /*local*/
        BC -pop                          // ; HL = the result of last grpprt_chr
           <- 0x08
        HL + BC
        BC -pop                          // ; DE = the result of last grpprt_chr
           -pop
        AF -pop
        return

    // ; A = Pattern , B = Pattern in VRAM
    grpprt_attr: /*local*/
        AF -push
        HL -push
        BC -push
        DE -push
        D <- A                           // ; D = Pattern of charactor
        E <- B                           // ; E = Pattern in VRAM
        BC <- [GRPCOL]
        HL + BC
        C <- A
        @[ATRBYT] & 0x0f -> B
        rdvrm()

        AF -push                         // ; A = an attribute in VRAM
        A & 0x0f -? B
        JR Z? grpprt_attr_black

        AF -pop -push
        A >* 4 & 0x0f -? B
        JR NZ? grpprt_attr_nomatch

        AF -pop

    grpprt_attr_end: /*local*/
        DE -pop
        BC -pop
        HL -pop
        AF -pop
        return

    grpprt_attr_black: /*local*/
        AF -pop

    grpprt_attr_blk_end: /*local*/
        DE -pop
        BC -pop
        HL -pop
        AF -pop
        A -not
        return

    grpprt_attr_nomatch: /*local*/
        @D | E -? 0xff
        JR Z? grpprt_attr_make_black

        AF -pop
        B <* 4
        A & 0x0f | B
        wrtvrm()
        JR grpprt_attr_end

    grpprt_attr_make_black: /*local*/
        AF -pop
        A & 0xf0 | B
        wrtvrm()
        JR grpprt_attr_blk_end

    // ; A = X MOD 8,C = Y MOD 8, HL = CLOC
    grpprt_chr: /*local*/
        B <- C
        A ++ -> C
        @0x07 ^ B ++ -> B

    grpprt_lp: /*local*/
        BC -push
        rdvrm()

        B <- A
        A <- [DE]
        CALL grpprt_attr

    grpprt_sft_lp: /*local*/
        C --
        JR Z? grpprt_sft_ed

        A >* 1
        JR grpprt_sft_lp

    grpprt_sft_ed: /*local*/
        C <- A
        @[CMASK] & C -> C                // ; charactor with mask
        A <- B                           // ; B = pattern in VRAM
          | C
        wrtvrm()

        HL ++
        DE ++
        BC -pop
        DJNZ grpprt_lp
        return
    }

    data grpprt_text = byte [ /*unused*/
        "GRPPRT" 0
    ]

    // ;--------------------------------
    // ; 0165h CHKNEW
    // ; Is the current screen mode a bitmap mode?
    // ; Output:  Carry flag set if current screen mode is SCREEN 5 or higher.
    // ; Changes: AF
    proc chknew() { /*unused*/
        @[SCRMOD] -? 5
        return
    }

    // ;--------------------------------
    // ; 016Bh BIGFIL
    // ; Fills VRAM with a fixed value.
    // ; Like FILVRM, but supports 128K of VRAM.
    // ; Input:   HL = VRAM start address
    // ;    (ACPAGE) = active VRAM page
    // ;          BC = number of bytes to fill
    // ;          A  = value to fill VRAM with
    // ; Changes: AF, BC
    proc bigfil() { /*unused*/
        AF -push
        nsetwr()

        BC --
        C ++
        A <- B
        B <- C
        C@A ++
        AF -pop
        DI

    bigfil_lp: /*local*/
        A -out VDP_DATA
        DJNZ bigfil_lp

        C --
        JR NZ? bigfil_lp
        EI
        return
    }

    // ;--------------------------------
    // ; 016Eh NSETRD
    // ; Set VRAM address and read mode.
    // ; Like SETRD, but supports 128K of VRAM.
    // ; Input:   HL = VRAM address
    // ;    (ACPAGE) = active VRAM page
    // ; Changes: AF
    // ; Note: If an odd-numbered 32K page is active and HL >= $8000,
    // ;       16-bit wrap around occurs.
    proc nsetrd() {
        nset_addr()

        @H & 0x3f -out VDP_ADDR          // ; A13..A8
        EI
        return
    }

    // ;--------------------------------
    // ; 0171h NSETWR
    // ; Set VRAM address and write mode.
    // ; Like SETWRT, but supports 128K of VRAM.
    // ; Input:   HL = VRAM address
    // ;    (ACPAGE) = active VRAM page
    // ; Changes: AF
    // ; Note: If an odd-numbered 32K page is active and HL >= $8000,
    // ;       16-bit wrap around occurs.
    proc nsetwr() {
        nset_addr()

        @H & 0x3f | 0x40 -out VDP_ADDR   // ; A13..A8
        EI
        return
    }

    proc nset_addr() {
        @[ACPAGE] | A
        JR Z? nset_32k

        @[SCRMOD] -? 5
        JP C? setwrt

        A -? 7 <- [ACPAGE]
        JR C? nset_32k                   // ; SCREEN5/6 -> 32K pages

        A + A                            // ; SCREEN7/8 -> 64K pages

    nset_32k: /*local*/
        HL -push
        A & 0x03                         // ; A  =  0   0   0   0   0   0   P1  P0
          >* 1 -> L                      // ; L  =  P0  0   0   0   0   0   0   P1
          & 0x80                         // ; A  =  P0  0   0   0   0   0   0   0
          ^ H                            // ; A  = A15 A14 A13 A12 A11 A10  A9  A8
          <*$ 1                          // ; CF = A15
        L <*$ 1                          // ; L  =  0   0   0   0   0   0   P1 A15
        A <*$ 1                          // ; CF = A14
          <- L <*$ 1                     // ; A  =  0   0   0   0   0   P1 A15 A14
        DI

        A -out VDP_ADDR                  // ; A16..A14
          <- 0x8e -out VDP_ADDR          // ; R#14
        HL -pop
        @L -out VDP_ADDR                 // ; A7..A0
        return
    }

    // ;--------------------------------
    // ; 0174h NRDVRM
    // ; Read a byte from VRAM.
    // ; Leaves the VRAM in read mode at the byte after the one read.
    // ; Like RDVRM, but supports 128K of VRAM.
    // ; Input:   HL = VRAM address
    // ;    (ACPAGE) = active VRAM page
    // ; Output:   A = the byte read
    proc nrdvrm() { /*unused*/
        nsetrd()

        A -in VDP_DATA
        return
    }

    // ;--------------------------------
    // ; 0177h NWRVRM
    // ; Write a byte to VRAM.
    // ; Leaves the VRAM in write mode at the byte after the one written.
    // ; Like WRTVRM, but supports 128K of VRAM.
    // ; Input:   HL = VRAM address
    // ;    (ACPAGE) = active VRAM page
    // ;           A = the byte to write
    proc nwrvrm() { /*unused*/
        AF -push
        nsetwr()

        AF -pop
        A -out VDP_DATA
        return
    }

    // ; VDP routines which only exist in sub rom, but are useful for C-BIOS internal
    // ; use as well:
    // ;-------------------------------------
    // ; $0131(sub) VDPSTA
    // ; Read VDP status register.
    // ; Input:   A = number of status register
    // ; Output:  A = value read
    // ; Changes: F
    proc vdpsta() { /*unused*/
        DI

        // ; Select desired status register.
        A -out VDP_ADDR <- (0x80 + 15)
          -out VDP_ADDR

        // ; Read status register.
        A -in VDP_STAT
        AF -push

        // ; Restore status register 0.
        A ^ A -out VDP_ADDR
        @(0x80 + 15) -out VDP_ADDR
        EI

        AF -pop
        return
    }

    // ;--------------------
    // ;Initializes VDP routine
    // ;--------------------

    proc init_vdp() {
        A -in VDP_STAT                   // ; reset latch
        BC <- 0x00                       // ; R#0
        wrtvdp()

        BC <- 0xe001                     // ; R#1
        wrtvdp()

        BC <- 0x02                       // ; R#2
        wrtvdp()

        BC <- 0x8003                     // ; R#3
        wrtvdp()

        BC <- 0x0104                     // ; R#4
        wrtvdp()

        BC <- 0x0808                     // ; R#8
        wrtvdp()

        @1 -> [CSRY] -> [CSRX]
        cls_screen1()

        A <- 0x00
        HL <- 0x0800
        BC <- 0x0800
        filvrm()

        // ; for screen 1 color table
        A <- 0xf5
        HL <- 0x2000
        BC <- 0x20
        filvrm()

        // ; PatGenTbl
        // ;        76543210 76543210
        // ;        00000100 00000000
        // ;             04h      00h

        BC <- 0xf507                     // ; R#7
        wrtvdp()

        HL <- B_Font
        DE <- 0x0800
        BC <- 0x0800
        ldirvm()
        return

        // ; TODO: Is it safe to enable this on MSX1 machines?
        // ;       Or can we autodetect the VDP?
        return
    }

    // ;------------------------------
    // ; Initialise font.
    // ; Uploads font to VRAM address specified by CGPBAS.
    proc init_font() {
        HL <- B_Font
        DE <- [CGPBAS]
        BC <- 0x0800
        JP ldirvm
    }

    // ;--------------------------------
    // ; $00C3 CLS
    // ; Clears the screen.
    // ; Input:   BAKCLR,
    // ;          Z-Flag has to be low if the main ROM version of CLS is called;
    // ;          in the sub ROM version of CVS the Z-Flag is ignored.
    // ; Changes: AF, BC, DE
    // ;TODO: add optional borders to text based screens
    // ;      -> Should that happen in CLS?
    proc cls_z() {
        return-if NZ?
        fallthrough
    }

    proc cls() {
        @[SCRMOD] -? 4
        return-if NC?                    // ; Out of range?

        HL -push <- cls_table
        jump_table()

        HL -pop
        return

        data cls_table = word [ /*local*/
            cls_screen0
            cls_screen1
            cls_screen2
            cls_screen3
        ]
    }

    proc cls_screen0() {
        @[LINLEN] -? 40
        BC <- (40 * 24)
        JR C? cls_text

        BC <- (80 * 24)
        JR cls_text
    }

    proc cls_screen1() {
        BC <- (32 * 24)
        fallthrough
    }

    proc cls_text() {
        HL <- [NAMBAS]
        A <- 0x20
        filvrm()

        A <- 1
        HL <- LINTTB
        A -> [HL]
        DE <- (LINTTB + 1)
        BC <- 23
        LDIR
        JP chput_ctrl_home
    }

    proc cls_screen2() {
        A ^ A
        BC <- 0x1800
        HL <- [CGPBAS]
        L <- A
        BC -push
        filvrm()

        BC -pop
        A <- [BAKCLR]
        HL <- [GRPCOL]
        JP filvrm
    }

    proc cls_screen3() {
        @[BAKCLR] & 0x0f -> B <* 4 | B
        BC <- 0x0800
        HL <- [CGPBAS]
        JP filvrm
    }

    // ; $0105 GETPAT
    // ; Function : Returns current pattern of a character
    // ; Input    : A  - ASCII code of character
    // ; Output   : Pattern in PATWRK starting from address #FC40
    // ; Registers: All
    // ; Remark   : Same as routine in MSX1-BIOS, but there it doesn't exist as
    // ;            a BIOS-call
    proc getpat() {
        BC <- [(CGPNT + 1)]
        L <- A
        H <- 0
        HL + HL + HL + HL + BC
        B <- 8
        DE <- PATWRK

    getpat_loop: /*local*/
        BC -push
        DE -push
        HL -push
        A <- [CGPNT]
        rdslt()

        HL -pop
        DE -pop
        BC -pop
        A -> [DE]
        DE ++
        HL ++
        DJNZ getpat_loop
        return
    }

    // ;--------------------------------
    // ; $00FC RIGHTC
    // ; Function : Shifts screenpixel to the right
    // ; Registers: AF
    // ; NOTE     : This implementation is still a stub!
    proc rightc() {
        HL -push
        AF -push
        HL <- rightc_text
        print_debug()

        AF -pop
        HL -pop
        return

        data rightc_text = byte [ /*local*/
            "RIGHTC" 0
        ]
    }

    // ;--------------------------------
    // ; $00FF LEFTC
    // ; Function : Shifts screenpixel to the left
    // ; Registers: AF
    // ; NOTE     : This implementation is still a stub!
    proc leftc() {
        HL -push
        AF -push
        HL <- leftc_text
        print_debug()

        AF -pop
        HL -pop
        return

        data leftc_text = byte [ /*local*/
            "LEFTC" 0
        ]
    }

    // ;--------------------------------
    // ; $0102 UPC
    // ; Function : Shifts screenpixel up
    // ; Registers: AF
    // ; NOTE     : This implementation is still a stub!
    proc upc() {
        HL -push
        AF -push
        HL <- upc_text
        print_debug()

        AF -pop
        HL -pop
        return

        data upc_text = byte [ /*local*/
            "UPC" 0
        ]
    }

    // ;--------------------------------
    // ; $0105 TUPC
    // ; Function : Tests whether UPC is possible, if possible, execute UPC
    // ; Output   : C-flag set if operation would end outside the screen
    // ; Registers: AF
    // ; NOTE     : This implementation is still a stub!
    proc tupc() {
        HL -push
        AF -push
        HL <- tupc_text
        print_debug()

        AF -pop
        HL -pop
        return

        data tupc_text = byte [ /*local*/
            "TUPC" 0
        ]
    }

    // ;--------------------------------
    // ; $0108 DOWNC
    // ; Function : Shifts screenpixel down
    // ; Registers: AF
    // ; NOTE     : This implementation is still a stub!
    proc downc() {
        HL -push
        AF -push
        HL <- downc_text
        print_debug()

        AF -pop
        HL -pop
        return

        data downc_text = byte [ /*local*/
            "DOWNC" 0
        ]
    }

    // ;--------------------------------
    // ; $010B TDOWNC
    // ; Function : Tests whether DOWNC is possible, if possible, execute DOWNC
    // ; Output   : C-flag set if operation would end outside the screen
    // ; Registers: AF
    // ; NOTE     : This implementation is still a stub!
    proc tdownc() {
        HL -push
        AF -push
        HL <- tdownc_text
        print_debug()

        AF -pop
        HL -pop
        return

        data tdownc_text = byte [ /*local*/
            "TDOWNC" 0
        ]
    }

    // ;--------------------------------
    // ; $010E SCALXY
    // ; Function : Scales X and Y coordinates
    // ; NOTE     : This implementation is still a stub!
    proc scalxy() {
        BC <- [GRPACX]
        DE <- [GRPACY]
        return
    }

    data scalxy_text = byte [ /*unused*/
        "SCALXY" 0
    ]

    // ;--------------------------------
    // ; $0111 MAPXYC
    // ; Function : Places cursor at current cursor address
    // ; Input    : BC = X coordinate,DE=Y coordinate
    // ; Register : AF,D,HL
    // ; NOTE     : This is a test version
    proc mapxy() {
        BC -push -> [GRPACX]
        DE -> [GRPACY]
        HL@[GRPACY] + HL + HL + HL
            + HL + HL
        L <- 0x00
        B <- 0x00
        @0xff -> [CMASK]
        @C & 0x07
        JR Z? mapxy_mask_ed

        B <- A
        A <- 0xff

    mapxy_mask_lp: /*local*/
        A & A >*$ 1
        DJNZ mapxy_mask_lp

        A -> [CMASK]

    mapxy_mask_ed: /*local*/
        @C & 0xf8 -> C
        B <- 0x00
        HL + BC -> [CLOC]
        BC -pop
        return
    }

    data mapxy_text = byte [ /*unused*/
        "MAPXY" 0
    ]

    // ;--------------------------------
    // ; $0114 FETCHC
    // ; Function : Gets current cursor addresses mask pattern
    // ; Output   : HL - Cursor address
    // ;            A  - Mask pattern
    proc fetchc() {
        A <- [CMASK]
        HL <- [CLOC]
        return
    }

    data fetchc_text = byte [ /*unused*/
        "FETCHC" 0
    ]

    // ;--------------------------------
    // ; $0117 STOREC
    // ; Function : Record current cursor addresses mask pattern
    // ; Input    : HL - Cursor address
    // ;            A  - Mask pattern
    // ; NOTE     : This implementation is still a stub!
    proc storec() {
        HL -push
        AF -push
        HL <- storec_text
        print_debug()

        AF -pop
        HL -pop
        return

        data storec_text = byte [ /*local*/
            "STOREC" 0
        ]
    }

    // ;--------------------------------
    // ; $011A SETATR
    // ; Function : Set attribute byte
    // ; NOTE     : This implementation is still a stub!
    proc setatr() {
        HL -push
        AF -push
        HL <- setatr_text
        print_debug()

        AF -pop
        HL -pop
        return

        data setatr_text = byte [ /*local*/
            "SETATR" 0
        ]
    }

    // ;--------------------------------
    // ; $011D READC
    // ; Function : Reads attribute byte of current screenpixel
    // ; NOTE     : This implementation is still a stub!
    proc readc() {
        HL -push
        AF -push
        HL <- readc_text
        print_debug()

        AF -pop
        HL -pop
        return

        data readc_text = byte [ /*local*/
            "READC" 0
        ]
    }

    // ;--------------------------------
    // ; $0120 SETC
    // ; Function : Returns currenct screenpixel of specificed attribute byte
    // ; NOTE     : This implementation is still a stub!
    proc setc() {
        HL -push
        AF -push
        HL <- setc_text
        print_debug()

        AF -pop
        HL -pop
        return

        data setc_text = byte [ /*local*/
            "SETC" 0
        ]
    }

    // ;--------------------------------
    // ; $0123 NSETCX
    // ; Function : Set horizontal screenpixels
    // ; NOTE     : This implementation is still a stub!
    proc nsetcx() {
        HL -push
        AF -push
        HL <- nsetcx_text
        print_debug()

        AF -pop
        HL -pop
        return

        data nsetcx_text = byte [ /*local*/
            "NSETCX" 0
        ]
    }

    // ;--------------------------------
    // ; $0126 GTASPC
    // ; Function : Gets screen relations
    // ; Output   : DE, HL
    // ; Registers: DE, HL
    // ; NOTE     : This implementation is still a stub!
    proc gtaspc() {
        HL -push
        AF -push
        HL <- gtaspc_text
        print_debug()

        AF -pop
        HL -pop
        return

        data gtaspc_text = byte [ /*local*/
            "GTASPC" 0
        ]
    }

    // ;--------------------------------
    // ; $0129 PNTINI
    // ; Function : Initalises the PAINT instruction
    // ; NOTE     : This implementation is still a stub!
    proc pntini() {
        HL -push
        AF -push
        HL <- pntini_text
        print_debug()

        AF -pop
        HL -pop
        return

        data pntini_text = byte [ /*local*/
            "PNTINI" 0
        ]
    }

    // ;--------------------------------
    // ; $012C SCANR
    // ; Function : Scans screenpixels to the right
    // ; NOTE     : This implementation is still a stub!
    proc scanr() {
        HL -push
        AF -push
        HL <- scanr_text
        print_debug()

        AF -pop
        HL -pop
        return

        data scanr_text = byte [ /*local*/
            "SCANR" 0
        ]
    }

    // ;--------------------------------
    // ; $012F SCANL
    // ; Function : Scans screenpixels to the left
    // ; NOTE     : This implementation is still a stub!
    proc scanl() {
        HL -push
        AF -push
        HL <- scanl_text
        print_debug()

        AF -pop
        HL -pop
        return

        data scanl_text = byte [ /*local*/
            "SCANL" 0
        ]
    }

    // ; vim:ts=8:expandtab:filetype=z8a:syntax=z8a:
    // -- end: video.asm

    // -- begin: debug.asm

    // ; C-BIOS debug routines
    // ; These routines should not be used in release builds of C-BIOS, but they can
    // ; be useful for developers and testers.
    // ;
    // ; Copyright (c) 2004 Maarten ter Huurne.  All rights reserved.
    // ;
    // ; 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.
    // ;
    // ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
    // ;

    // ;--------------------------------
    // ; Print string to openMSX debug device as a separate line.
    // ; Input:   HL = address of zero-terminated string
    // ; Changes: HL, AF
    proc print_debug() {
        @0x23 -out DBG_CTRL
        CALL print_debug_asciiz

        @0x00 -out DBG_CTRL
        return

    // ;--------------------------------
    // ; Print string to openMSX debug device  as part of an existing line.
    // ; Input:   HL = address of zero-terminated string
    // ; Changes: HL, AF
    print_debug_asciiz: /*local*/
        A <- [HL]
        HL ++
        A | A
        return-if Z?

        A -out DBG_DATA
        JR print_debug_asciiz
    }

    // ;--------------------------------
    // ; Print nibble in hexidecimal format as part of an existing line.
    // ; Input:   A = nibble to print (bit 7-4 are ignored)
    // ; Changes: AF
    proc print_debug_hexnibble() {
        A & 0x0f -? 10
        JR NC? print_debug_hexnibble_letter

        A + '0' -out DBG_DATA
        return

    print_debug_hexnibble_letter: /*local*/
        A + ('A' - 10) -out DBG_DATA
        return
    }

    // ;--------------------------------
    // ; Print byte in hexidecimal format as part of an existing line.
    // ; Input:   A = byte to print
    // ; Changes: AF
    proc print_debug_hexbyte() {
        AF -push
        A >* 4
        print_debug_hexnibble()

        AF -pop
        print_debug_hexnibble()
        return
    }

    // ;--------------------------------
    // ; Print word in hexidecimal format as part of an existing line.
    // ; Input:   HL = word to print
    // ; Changes: HL, AF
    proc print_debug_hexword() { /*unused*/
        A <- H
        print_debug_hexbyte()

        A <- L
        print_debug_hexbyte()
        return
    }

    // -- end: debug.asm

    // ; The game "Hacker" jumps directly to this location($0D02).
    // ; Star force calls $0D0E.

    org> 0x0d01

    // ; for all wrong jumper,put RET instruction there
    RET
    POP IX                               // ; $0D02
    POP IY
    POP AF
    POP BC
    POP DE
    POP HL
    EXX
    EX AF AF-
    POP AF
    POP BC
    POP DE
    POP HL
    EI
    RET

    // ; $0000 CHKRAM
    // ; Function : Tests RAM and sets RAM slot for the system
    // ; Registers: All
    // ; Remark   : After this, a jump must be made to INIT, for further initialisation.
    proc chkram() {
        // ; Initialize interface
        @0x82 -out PPI_REGS
        @0x50 -out GIO_REGS

        // ; Initialize memory bank
        A ^ A -out MAP_REG4 ++
          -out MAP_REG3 ++
          -out MAP_REG2 ++
          -out MAP_REG1

        // ; Select the longest contiguous memory area for pages 3 and 2.
        HL <- 0xffff                     // ; Keep the current best values in the
        EXX                              // ; alternative register set (HL and BC).

        // ; For each primary slot:
        A -in PSL_STAT | 0xf0 -> B

    chkram_pslot: /*local*/
        // ; Select primary slot.
        @B -out PSL_STAT

        // ; For each secondary slot:
        // ; Note that we do not check if the secondary slot is actually
        // ; available: in case there is no secondary slot, we do the same
        // ; test four times for the same primary slot.
        @[SSL_REGS] -not | 0xf0 -> C

    chkram_sslot: /*local*/
        @C -> [SSL_REGS]

        // ; Find the longest contiguous memory area for this slot
        // ; configuration.  Note that there is always ROM in pages
        // ; 1 and 0.
        HL <- 0xff00

    chkram_find: /*local*/
        @0x0f -> [HL] -? [HL]
        JR NZ? chkram_find_end

        A -not -> [HL] -? [HL]
        JR NZ? chkram_find_end

        H --
        JR chkram_find

    chkram_find_end: /*local*/
        H ++

        // ; Update the best values.
        @H | A
        JR Z? chkram_sslot_end
        EXX

        A -? H
        JR C? chkram_update
        JR Z? chkram_update
        EXX
        JR chkram_sslot_end

    chkram_update: /*local*/
        L <- 0                           // ; Fix the L register to indicate that
        H <- A                           // ; RAM is found.
        EXX

        A <- B
        EXX

        B <- A
        EXX

        A <- C
        EXX

        C <- A
        EXX

    chkram_sslot_end: /*local*/
        @C - 0x10 -> C
        JR NC? chkram_sslot

    chkram_pslot_end: /*local*/
        @B - 0x10 -> B
        JR NC? chkram_pslot

        // ; Select the longest contiguous memory area.
        EXX

        @L | A
        JR Z? chkram_select

        DE <- str_memory_err
        JP print_error

    chkram_select: /*local*/
        @B -out PSL_STAT
        @C -> [SSL_REGS]

        // ; HL contains the start of the memory area (for BOTTOM variable).
        EXX

        // ;----------------------
        // ; User interface
        // ;----------------------

        HL@0xf300 -> SP                  // ; set $F300 to stack pointer
        CALL init_ram
        CALL check_expanded
        init_vdp()
        EI
        initio()

        B <- 15
        DE <- logo_ident
        HL <- 0x8000

    logo_check: /*local*/
        BC -push
        HL -push
        DE -push
        A <- [EXPTBL]
        rdslt()

        HL -pop
        DE -pop
        BC -pop
        A -? [HL]
        JR NZ? logo_none

        DE <-> HL ++
        HL ++
        DJNZ logo_check

        IX <- 0x8010
        IY <- [(EXPTBL - 1)]
        calslt()
        JR logo_done

    logo_none: /*local*/
        @5 -> [BAKCLR] -> [BDRCLR]
        init32()

        HL <- str_proginfo
        prn_text()

    logo_done: /*local*/
        EI

        B <- 120
        CALL wait_b

        @COLOR_BORDER -> [BDRCLR]
        @COLOR_BACK -> [BAKCLR]
        @COLOR_FORE -> [FORCLR]
        @29 -> [LINL32]
        init32()

        HL <- str_proginfo
        prn_text()
        CALL search_roms
        CALL H_STKE
        CALL run_basic_roms

        // ; Set up hooks and system vars so NMS8250 disk ROM will try
        // ; to load and execute the boot sector.
        @1 -> [DEVICE] ^ A

        // ; TODO: Find out or invent name for $FB29.
        A -> [0xfb29]

        // ; This is the hook the disk ROM uses for booting.
        CALL H_RUNC

        // ; We couldn't boot anything, instead show disk contents.
        // ; TODO: This breaks boot of MG2, so disabled for now.
        // ;                jp      disk_intr
        // ;                ret                     ; goto stack_error

        HL <- str_nocart
        prn_text()
        JP hang_up_mode

        data logo_ident = byte [ /*local*/
            "C-BIOS Logo ROM"
        ]

    // ;----------------------
    // ; Search for any extension ROMs and initialize them.
    search_roms: /*local*/
        // ; Clear SLTATR.
        HL <- SLTATR
        DE <- (SLTATR + 1)
        BC <- (4 * 4 * 4 - 1)
        $(0) -> [HL]
        LDIR

        // ; Search for ROMs.
        HL <- EXPTBL
        A ^ A                            // ; A = input for RDSLT

    search_roms_lp: /*local*/
        HL -push
        A | [HL]

    search_roms_lp_sub: /*local*/
        HL <- 0x4000
        CALL search_roms_check
        CALL Z? search_roms_init

        HL <- 0x8000
        CALL search_roms_check
        CALL Z? search_roms_init

    search_roms_no: /*local*/
        A -bit? 7
        JR Z? search_roms_next_slot

        A + 4                            // ; Select next subslot.
          -bit? 4
        JR Z? search_roms_lp_sub

    search_roms_next_slot: /*local*/
        HL -pop                          // ; Select next slot.
           ++
        A ++ & 0x03
        JR NZ? search_roms_lp
        return

    // ; Helper routine to read two bytes from a given slot.
    search_roms_read: /*local*/
        B <- A                           // ; Save the input for RDSLT in B.
        rdslt()

        HL ++
        AF -push
        A <- B
        rdslt()

        HL ++
        D <- A
        AF -pop
        E <- A
        A <- B
        return

    // ; Check whether the ROM is present or not.
    search_roms_check: /*local*/
        HL -push
        CALL search_roms_read

        HL <- 0x4241                     // ; "AB"
        dcompr()                         // ; ZF is set if the ROM is present.

        A <- B
        HL -pop
        return

    // ; Initialize the ROM and set up the related system variables.
    search_roms_init: /*local*/
        // ; Output a message to show that a ROM is found.
        HL -push
        AF -push
        HL <- str_slot
        prn_text()

        AF -pop -push
        B <- A
        A & 0x03 + '0'
        chput()

        A <- B
        B -bit? 7
        JR Z? search_roms_init_skip

        A <- '.'
        chput()

        @B >* 2 & 0x03 + '0'
        chput()

    search_roms_init_skip: /*local*/
        A <- 0x0d
        chput()

        A <- 0x0a
        chput()

        AF -pop
        HL -pop

        // ; Read the initialization address and initialize the ROM.
        HL ++ ++
        CALL search_roms_address
        JR Z? search_roms_init_statement

        // ; Reg.C is using some games for slot number
        C <- A
        DE -push
        IX -pop
        AF -push
        IY -pop

    // ; Some cartridges have buggy initialisation code.
    // ; By postponing the interrupt as long as possible,
    // ; there is a better chance they will boot correctly.
    // ; For example the game "Koronis Rift" depends on this.
    search_roms_init_waitv: /*local*/
        A -in 0x99 | A
        JP M? search_roms_init_waitv

        AF -push
        HL -push
        calslt()
        DI

        HL -pop
        AF -pop

    // ; Check if the addresses are valid.
    search_roms_init_statement: /*local*/
        C <- 0
        CALL search_roms_address
        JR Z? search_roms_init_device

        C -set 5

    search_roms_init_device: /*local*/
        CALL search_roms_address
        JR Z? search_roms_init_basic

        C -set 6

    search_roms_init_basic: /*local*/
        CALL search_roms_address
        JR Z? search_roms_init_variables

        C -set 7

    // ; Set up the related system variables.
    search_roms_init_variables: /*local*/
        B <- A                           // ; A = x000sspp
        A & 0x0c -> E
        @B <* 4 & 0x30 | E               // ; A = 00ppss00
           -> E
        @H <* 2 & 0x03 | E               // ; A = 00ppssPP
        HL <- SLTATR
        D <- 0
        E <- A
        HL + DE
        C -> [HL]
        A <- B
        return

    // ; Read an address and check whether it is valid or not.
    search_roms_address: /*local*/
        BC -push
        CALL search_roms_read

        @D | E                           // ; ZF is not set if the address is
        A <- B                           // ; correct.
        BC -pop
        return

    // ;----------------------
    // ; Run any BASIC roms found.
    run_basic_roms: /*local*/
        HL <- SLTATR
        B <- 64

    run_basic_roms_lp: /*local*/
        @[HL] -bit? 7
        JR Z? run_basic_roms_next

        HL -push <- str_basic
        prn_text()

        HL -pop

    run_basic_roms_next: /*local*/
        HL ++
        DJNZ run_basic_roms_lp
        return

    // ;------------------------
    // ; Initialize RAM

    init_ram: /*local*/
        // ; Initialize workarea
        A <- 0x00
        HL <- 0xf380
        A -> [HL]
        DE <- 0xf381
        BC <- 0x0c7d
        LDIR

        // ; Initialize Disk work
        A <- 0xc9
        HL <- 0xf300
        A -> [HL]
        DE <- 0xf301
        BC <- 0x7f
        LDIR

        // ; initialize hook area with $C9 (assembler code for ret)
        A <- 0xc9                        // ; ret code
        HL <- H_KEYI
        A -> [HL]
        DE <- (H_KEYI + 1)
        BC <- 0x024d                     // ; shouldn't this be $0235 ?
        LDIR

        // ; Initialize key matrix
        A <- 0xff
        HL <- OLDKEY
        A -> [HL]
        DE <- (OLDKEY + 1)
        BC <- 21
        LDIR

        // ; Initialize Key buffer
        A <- 0x00
        HL <- KEYBUF
        A -> [HL]
        DE <- (KEYBUF + 1)
        BC <- 39
        LDIR

        // ; Set address pointer
        HL@KEYBUF -> [PUTPNT]
            -> [GETPNT]

        // ;                ld      hl,$8000
        EXX

        HL -> [BOTTOM]                   // ; Page1 and 2 is ROM,Page3 and 4 is RAM.
        EXX

        // ; I don't know exactly what is stored between $F168 and $F380,
        // ; but the disk ROM needs some space there, so I'll just
        // ; reserve all of it.
        HL <- 0xf380                     // ; was $F168, but needs to be changed by disk ROM
           -> [HIMEM]                    // ; limit of usable memory
           -> [STKTOP]                   // ; position of BASIC stack

        // ;Transmit RDPRIM to RAM.

        HL <- m_rdprim
        DE <- 0xf380
        BC <- (m_prim_end - m_rdprim)
        LDIR

        // ; Initialize table of screen 0
        HL@0x00 -> [TXTNAM]
        HL@0x0800 -> [TXTCGP]

        // ; Initialize table of screen 1
        HL@0x1800 -> [T32NAM]
        HL@0x2000 -> [T32COL]
        HL@0x00 -> [T32CGP]
        HL@0x1b00 -> [T32ATR]
        HL@0x3800 -> [T32PAT]

        // ; Initialize table of screen 2

        HL@0x1800 -> [GRPNAM]
        HL@0x2000 -> [GRPCOL]
        HL@0x00 -> [GRPCGP]
        HL@0x1b00 -> [GRPATR]
        HL@0x3800 -> [GRPPAT]

        // ; Initialize table fo screen 3
        HL@0x0800 -> [MLTNAM]
        HL@0x00 -> [MLTCGP]
        HL@0x1b00 -> [MLTATR]
        HL@0x3800 -> [MLTPAT]

        // ; Initialise QUETAB.
        HL@QUETAB -> [QUEUES]
        HL@VOICAQ
            -> [(QUETAB + 0 * 6 + 4)]
        HL@VOICBQ
            -> [(QUETAB + 1 * 6 + 4)]
        HL@VOICCQ
            -> [(QUETAB + 2 * 6 + 4)]
        @0x7f
            -> [(QUETAB + 0 * 6 + 3)]
            -> [(QUETAB + 1 * 6 + 3)]
            -> [(QUETAB + 2 * 6 + 3)]

        // ; other settings
        @39 -> [LINL40]
        A <- 32                          // ; Set to 29 after splash screen.
          -> [LINL32]

        // ;TODO: Rely on call to INIT32 instead.
        @[LINL32] -> [LINLEN]
        @24 -> [CRTCNT]
        @COLOR_BORDER -> [BDRCLR]
        @COLOR_BACK -> [BAKCLR]
        @COLOR_FORE -> [FORCLR]
        @0xa0 -> [RG1SAV]
        @[EXPTBL] -> [CGPNT]
        HL@[4] -> [(CGPNT + 1)]

        // ; set up hook
        A <- 0xc3
        HL@chput -> [(H_OUTD + 1)]
        A -> [H_OUTD]
        return

    // ;----------------------
    // ; Check which slots are expanded.
    // ; Initialises EXPTBL for all 4 slots.
    check_expanded: /*local*/
        // ; Prepare to iterate over slots [0..3].
        DI

        HL <- EXPTBL
        A -in PSL_STAT -> D              // ; D = saved value from port $A8
          & 0x3f -> C

    check_expanded_lp: /*local*/
        A -out PSL_STAT <- [SSL_REGS]
          -not -> E                      // ; E = saved SSL value

        // ; Test whether $0x is read back as complement.
        A & 0x0f -> [SSL_REGS] -> B
        @[SSL_REGS] -not -? B
        JR NZ? check_expanded_not

        // ; Test whether $5x is read back as complement.
        @E & 0x0f | 0x50 -> [SSL_REGS]
           -> B
        @[SSL_REGS] -not -? B
        JR NZ? check_expanded_not

        // ; SSL register present -> slot expanded.
        B <- 0x80
        A <- E
        JR check_expanded_next

    check_expanded_not: /*local*/
        // ; SSL register present -> slot expanded.
        B <- 0x00
        A <- E                           // ; E = saved SSL value
          -not                           // ; not SSL -> back to original

    check_expanded_next: /*local*/
        A -> [SSL_REGS] <- D             // ; D = saved value from port $A8
          -out PSL_STAT
        B -> [HL]
        HL ++

        // ; Next slot.
        @C + 0x40 -> C
        JR NC? check_expanded_lp
        EI
        return

    // ;------------------------
    // ; wait routine
    // ; caution,already EI when call the rouine
    // ; B = frequency of loop
    wait_b: /*local*/
        HALT
        DJNZ wait_b
        return
    }

    // ;------------------------
    // ;prn_text
    // ; HL = string with null termination

    proc prn_text() {
        @[SCRMOD] -? 5
        JR NC? prn_text_graph

    prn_text_char: /*local*/
        @[HL] | A
        return-if Z?
        chput()

        HL ++
        JR prn_text_char

    prn_text_graph: /*local*/
        @[HL] | A
        return-if Z?

        IX <- 0x89
        extrom()

        HL ++
        JR prn_text_graph
    }

    // ;--------------------------------
    // ; Determine bytes per line in the current text mode.
    // ; Input:   SCRMOD, LINLEN
    // ; Output:  C = number of bytes per line
    // ; Changes: AF
    proc text_bytes_per_line() {
        C <- 32                          // ; text32
        @[SCRMOD] | A
        return-if NZ?

        C <- 40                          // ; text40
        @[LINLEN] -? 41
        return-if C?

        C <- 80                          // ; text80
        return
    }

    // ;--------------------------------
    // ; Calculate the VRAM address that corresponds to the current cursor position.
    // ; Input:   CSRX, CSRY
    // ; Output:  HL = VRAM address
    // ; Changes: none
    proc curs2hl() {
        BC -push
        AF -push
        text_bytes_per_line()

        // ; Calculate left border.
        @[LINLEN] -neg + C               // ; A = bytes_per_line - LINLEN
            ++                           // ; round up
            >>> 1                        // ; A = A / 2
            -> L                         // ; L = size of left border

        // ; Add X coordinate.
        @[CSRX] --                       // ; from 1-based to 0-based
            + L                          // ; add border size
            -> L

        // ; Convert to 16-bits counters.
        H@0 -> B

        // ; Add Y * bytes_per_line.
        @[CSRY] --                       // ; from 1-based to 0-based

    curs2hl_mult_loop: /*local*/
        A >>> 1
        JR NC? curs2hl_mult_skip

        HL + BC

    curs2hl_mult_skip: /*local*/
        C << 1                           // ; BC = BC * 2
        B <*$ 1
        A | A
        JR NZ? curs2hl_mult_loop

        // ; Add base address.
        BC <- [NAMBAS]
        HL + BC
        AF -pop
        BC -pop
        return
    }

    // ;---------------------------
    // ; Subroutines
    // ;---------------------------

    // ; the extensive descriptions were taken with permission from http://map.tni.nl/

    // ;-------------------------------------
    // ;0008h SYNCHR
    // ;Function:  tests whether the character of [HL] is the specified character
    // ;           if not, it generates SYNTAX ERROR, otherwise it goes to CHRGTR
    // ;           (#0010)
    // ;Input:     set the character to be tested in [HL] and the character to be
    // ;           compared next to RST instruction which calls this routine (inline
    // ;           parameter)
    // ;Output:    HL is increased by one and A receives [HL], When the tested character
    // ;           is numerical, the CY flag is set the end of the statement (00h or
    // ;           3Ah) causes the Z flag to be set
    // ;Registers: AF, HL
    // ;NOTE: this implementation is still a stub!
    proc synchr() {
        HL -push
        AF -push
        HL <- synchr_text
        print_debug()

        AF -pop
        HL -pop
        return

        data synchr_text = byte [ /*local*/
            "SYNCHR" 0
        ]
    }

    // ;-------------------------------------
    // ; $0010 CHRGTR
    // ; Read the next program character.
    // ; In:      HL = pointer to the program text
    // ; Out:     A  = the next program character
    // ;          HL = pointer to the next program character
    // ;          ZF = set if it's the end of statement
    // ;          CF = set if it's a number
    // ; Changes: AF, HL
    proc chrgtr() {
        CALL H_CHRG

    chrgtr_lp: /*local*/
        A <- [HL]
        HL ++

        // ; Check for the end of statement.
        A -? 0x00                        // ; end of line
        return-if Z?

        A -? 0x3a                        // ; statement separator
        return-if Z?

        // ; Check for digits.
        A -? '0'
        JR C? chrgtr_no_digit

        A -? ('9' + 1)
        return-if C?

    chrgtr_no_digit: /*local*/
        // ; Skip whitespace.
        A -? 0x20                        // ; space
        JR Z? chrgtr_lp

        A -? 0x09                        // ; tab
        JR Z? chrgtr_lp

        // ; Otherwise it's a normal program character.
        A | A                            // ; Clear CF and ZF.
        return
    }

    // ;-------------------------------------
    // ; $0018 OUTDO
    // ; Function : Output to current outputchannel (printer, diskfile, etc.)
    // ; Input    : A  - PRTFIL, PRTFLG
    // ; Remark   : Used in basic, in ML it's pretty difficult.
    proc outdo() {
        AF -push
        CALL H_OUTD                      // ; H_OUTD does the real outputting

        AF -pop
        return
    }

    // ;--------------------------------
    // ; $0020 DCOMPR
    // ; Function : Compared HL to DE
    // ; Output   : flags influenced like CP instruction
    // ; Registers: A
    proc dcompr() {
        @H -? D
        return-if NZ?

        @L -? E
        return
    }

    // ;--------------------------------
    // ; $0028 GETYPR
    // ; Function : Returns Type of DAC
    // ; Input    : VALTYP(F663)
    // ; Output   : C, Z, S
    // ;       C       Z       S       Type    VALTYP
    // ;       low     -       -       double  8
    // ;       high    high    low     string  3
    // ;       high    low     high    integer 2
    // ;       high    low     low     float   4
    // ; Registers: AF
    // ;NOTE: this implementation is still a stub!
    proc getypr() {
        HL -push
        AF -push
        HL <- getypr_text
        print_debug()

        AF -pop
        HL -pop
        return

        data getypr_text = byte [ /*local*/
            "GETYPR" 0
        ]
    }

    // ;--------------------------------
    // ; $0030 CALLF
    proc callf() {
        AF <-> AF-
        EXX

        HL -pop                          // ; Get data from return address.
        A <- [HL]
        HL ++
        E <- [HL]
        HL ++
        D <- [HL]
        HL ++
        DE -push                         // ; IX = call address
        IX -pop
        AF -push                         // ; IY = slot
        IY -pop
        HL -push                         // ; Update return address.
        AF <-> AF-
        EXX
        JP calslt                        // ; Perform inter-slot call.
    }

    // ;--------------------------------
    // ; $003B INITIO
    // ;Function:  Initialises the device
    // ;Registers: All
    proc initio() {
        E <- 0x8f                        // ; strobe off, triggers on
        A <- 0x0f
        wrtpsg()

        // ; TODO: What else must be initialized here?

        JP gicini
    }

    // ;--------------------------------
    // ; $003E INIFNK
    // ; Function : Initialises the contents of the function keys
    // ; Registers: All
    // ;NOTE: this implementation is still a stub!
    proc inifnk() {
        HL -push
        AF -push
        HL <- inifnk_text
        print_debug()

        AF -pop
        HL -pop
        return

        data inifnk_text = byte [ /*local*/
            "INIFNK" 0
        ]
    }

    // ;--------------------------------
    // ; $0099 STRTMS
    // ; Function : Tests whether the PLAY statement is being executed as a background
    // ;            task. If not, begins to execute the PLAY statement
    // ; Registers: All
    // ;NOTE: this implementation is still a stub!
    proc strtms() {
        HL -push
        AF -push
        HL <- strtms_text
        print_debug()

        AF -pop
        HL -pop
        return

        data strtms_text = byte [ /*local*/
            "STRTMS" 0
        ]
    }

    // ;--------------------------------
    // ; $009C CHSNS
    // ; Function : Tests the status of the keyboard buffer
    // ; Output   : Z-flag set if buffer is filled
    // ; Registers: AF
    proc chsns() {
        EI

        HL -push
        DE -push
        HL <- [GETPNT]
        DE <- [PUTPNT]
        RST 0x20

        A <- 0xff
        JR NZ? chsns_inbuf

        A ^ A

    chsns_inbuf: /*local*/
        DE -pop
        HL -pop
        return
    }

    // ;--------------------------------
    // ; $009F CHGET
    // ; Function : One character input (waiting)
    // ; Output   : A  - ASCII-code of the input character
    // ; Registers: AF

    proc chget() {
        CALL H_CHGE

        HL -push
        DE -push

    chget_wait: /*local*/
        HL <- [GETPNT]
        DE <- [PUTPNT]
        RST 0x20
        JR NZ? chget_char
        EI
        HALT
        JR chget_wait

    chget_char: /*local*/
        A <- [HL]                        // ; HL = (GETPNT)
        AF -push
        HL ++

        // ; See comment in keyint (below label key_store).
        A <- L

        // ; Currently, tniASM doesn't support "&" and SjASM doesn't
        // ; support "AND", so we have to hardcode the result.
        // ;                cp      $00FF & (KEYBUF + 40)
        A -? 0x18
        JR NZ? chget_nowrap

        HL <- KEYBUF

    chget_nowrap: /*local*/
        HL -> [GETPNT]
        AF -pop
        DE -pop
        HL -pop
        return
    }

    // ;--------------------------------
    // ; $00A2 CHPUT
    // ; Input:   A = character code
    // ; Changes: none

    // -- begin: chput.asm

    // ; CHPUT routine for C-BIOS
    // ;
    // ; Copyright (c) 2006 Eric Boon.  All rights reserved.
    // ;
    // ; 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.
    // ;
    // ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
    // ;
    // ; -------------------------------------
    // ; $00A2 CHPUT
    // ; Function : Output character in A to screen
    // ; Input    : A = character code
    // ; Output   : -
    // ; Changes  : -

    proc chput() {
        HL -push                         // ; save all regs
        DE -push
        BC -push
        AF -push
        CALL H_CHPU                      // ; call hook

        A <- [SCRMOD]                    // ; this only works in
          -? 2                           // ; screen modes 0 and 1
        JR NC? chput_exit

        AF -pop                          // ; restore char to put in A
           -push
        chput_remove_cursor()

        AF -pop                          // ; restore char to put in A
           -push
        CALL chput_decode_char
        chput_restore_cursor()

        A <- [CSRX]                      // ; CSRX -> TTYPOS
          -> [TTYPOS]

    chput_exit: /*local*/
        AF -pop
        BC -pop
        DE -pop
        HL -pop
        return

    // ; -- decode character in A
    chput_decode_char: /*local*/
        cnvchr()                         // ; Graphic character?
        return-if NC?                    // ; NC  -> graphic extension hdr
        JR Z? chput_putchar              // ; C,Z -> graphic character

        AF -push                         // ; (C,NZ -> normal char)
        A <- [ESCCNT]                    // ; ESC sequence?
          | A
        JP NZ? chput_escape

        AF -pop
        A -? 0x20                        // ; Control code?
        JR C? chput_ctrl_search

        A -? 127                         // ; DEL?
        JP Z? chput_erase
        fallthrough
    }

    // ; -- print a normal character and move cursor to next position
    proc chput_putchar() {
        curs2hl()                        // ; output character to screen
        wrtvrm()

        HL <- [CSRY]                     // ; h = (CSRX), l = (CSRY)
        A <- [LINLEN]
        H ++
        A -? H
        JR C? chput_continue_line

        HL -> [CSRY]
        return

    chput_continue_line: /*local*/
        DE <- (LINTTB - 1)               // ; make logical line continue
        H <- 0
        HL + DE
        A ^ A -> [HL]
        HL <- [CSRY]                     // ; move cursor to start of
        chput_ctrl_cr()                  // ; new line
        JP chput_ctrl_lf
    }

    // ; -- Handle control code
    proc chput_ctrl_search() {
        B <- 12
        HL <- chput_ctrl_table
        JP search_table
    }

    // ; -- Fill with spaces until next TAB stop
    proc chput_ctrl_tab() {
        A <- 0x20
        chput_putchar()

        @[CSRX] & 7 -? 1
        JR NZ? chput_ctrl_tab
        return
    }

    // ; -- Line Feed.
    proc chput_ctrl_lf() {
        HL <- [CSRY]
        A <- [CRTCNT]
        L ++
        A -? L
        JR NC? chput_ctrl_lf_done

        HL -push
        CALL chput_ctrl_home             // ; home cursor
        chput_esc_m()                    // ; delete top line (scroll!)

        HL -pop
        L --

    chput_ctrl_lf_done: /*local*/
        HL -> [CSRY]
        return
    }

    // ; -- Home cursor
    chput_ctrl_home:
    proc chput_esc_h() {
        HL@0x0101 -> [CSRY]
        return
    }

    // ; -- Form Feed / Cls
    const chput_ctrl_ff = cls
    const chput_esc_e = cls
    const chput_esc_j = cls

    // ; -- Clear till end of screen
    proc chput_esc_jj() {
        chput_esc_k()                    // ; clear till end of line

        HL <- [CSRY]                     // ; save current cursor pos
           -push
        chput_ctrl_cr()                  // ; move to line start

    chput_esc_jj_loop: /*local*/
        A <- [CSRY]                      // ; while no at end of screen
        HL <- CRTCNT
        A -? [HL]
        JR NC? chput_esc_jj_done
        chput_ctrl_lf()                  // ;   move to next line
        chput_esc_k()                    // ;   clear till end of line
        JR chput_esc_jj_loop             // ; loop

    chput_esc_jj_done: /*local*/
        HL -pop                          // ; restore cursor pos
           -> [CSRY]
        return
    }

    // ; -- Carriage return
    proc chput_ctrl_cr() {
        @1 -> [CSRX]
        return
    }

    // ; -- Escape
    proc chput_ctrl_esc() {
        @0xff -> [ESCCNT]
        return
    }

    // ; -- Cursor right
    proc chput_ctrl_right() {
        A <- [CSRX]
        HL <- LINLEN
        A -? [HL]
        JR NC? chput_ctrl_right_next

        A ++
        JR chput_right_left_ok

    chput_ctrl_right_next: /*local*/
        A <- [CSRY]
        HL <- CRTCNT
        A -? [HL]
        return-if NC?

        A ++ -> [CSRY]
        JR chput_ctrl_cr
    }

    // ; -- Cursor left
    chput_ctrl_bs:
    proc chput_ctrl_left() {
        @[CSRX] --
        JR NZ? chput_right_left_ok

        @[CSRY] --
        return-if Z?

        A -> [CSRY] <- [LINLEN]
        fallthrough
    }

    proc chput_right_left_ok() {
        A -> [CSRX]
        return
    }

    // ; -- Cursor up
    chput_ctrl_up:
    proc chput_esc_a() {
        @[CSRY] --
        return-if Z?

        A -> [CSRY]
        return
    }

    // ; -- Cursor down
    chput_ctrl_down:
    proc chput_esc_b() {
        A <- [CSRY]
        HL <- CRTCNT
        A -? [HL]
        return-if NC?

        A ++ -> [CSRY]
        return
    }

    // ; -- Handle ESC mode (ESCCNT in A and != 0)
    proc chput_escape() {
        B <- A                           // ; b := (ESCCNT)
        A ++                             // ; (ESCCNT) == -1 ?
        JR NZ? chput_escape_1

        A -> [ESCCNT]
        AF -pop                          // ; restore character in A
        B <- 15                          // ; search in table
        HL <- chput_esc_table
        JP search_table

    chput_escape_1: /*local*/            // ; ----------------------------
        AF -pop
        DJNZ chput_escape_2

        // ; -- ESCCNT == 1: 'ESC x <n>'
        C <- 0                           // ; CSTYLE/CSRSW := 0
        JR chput_esc_xy

    chput_escape_2: /*local*/            // ; ----------------------------
        DJNZ chput_escape_3

        // ; -- ESCCNT == 2: 'ESC y <n>'
        C <- 1                           // ; CSTYLE/CSRSW := 1

    chput_esc_xy: /*local*/
        A -? '4'
        JR Z? chput_esc_xy_4

        A -? '5'
        JR Z? chput_esc_xy_5
        JR chput_escape_reset

    chput_esc_xy_4: /*local*/
        @C -> [CSTYLE]
        JR chput_escape_reset

    chput_esc_xy_5: /*local*/
        @C -> [CSRSW]
        JR chput_escape_reset

    chput_escape_3: /*local*/            // ; ----------------------------
        DJNZ chput_escape_4

        // ; -- ESCCNT == 3: 'ESC Y <n> <m>'
        B <- 0x1f
        A - B -> [CSRX]
        JR chput_escape_reset

    chput_escape_4: /*local*/            // ; ----------------------------
        DJNZ chput_escape_reset

        // ; -- ESCCNT == 4: 'ESC Y <n>'
        B <- 0x1f
        A - B -> [CSRY]
        A <- 3
        JR chput_escape_set
    }

    // ; -- ESCCNT := 1
    proc chput_esc_x() {
        A <- 1
        JR chput_escape_set
    }

    // ; -- ESCCNT := 2
    proc chput_esc_y() {
        A <- 2
        JR chput_escape_set
    }

    // ; -- ESCCNT := 4
    proc chput_esc_yy() {
        A <- 4
        JR chput_escape_set
    }

    proc chput_escape_reset() {
        A ^ A
        fallthrough
    }

    proc chput_escape_set() {
        A -> [ESCCNT]
        return
    }

    // ; -- Cursor right, no wrap
    proc chput_esc_c() {
        A <- [CSRX]
        HL <- LINLEN
        A -? [HL]
        return-if NC?

        A ++ -> [CSRX]
        return
    }

    // ; -- Cursor left, no wrap
    proc chput_esc_d() {
        @[CSRX] --
        return-if Z?

        A -> [CSRX]
        return
    }

    // ; -- clear line
    proc chput_esc_l() {
        chput_ctrl_cr()
        fallthrough
    }

    // ; -- Clear till end of line
    proc chput_esc_k() {
        HL <- (LINTTB - 1)               // ; update LINTTB
        @[CSRY] -> E
        D <- 0
        HL + DE

        // ; a != 0, which is OK
        A -> [HL] <- [LINLEN] ++         // ; because CSRX is 1-based
        HL <- CSRX
        A - [HL] -> C
        B <- 0
        A <- 32
        curs2hl()
        JP filvrm
    }

    // ; -- Insert line
    proc chput_esc_ll() {
        chput_ctrl_cr()                  // ; move to start of line

        HL <- [CSRY]                     // ; save current cursor pos
           -push
        B <- L
        @[CRTCNT] -> [CSRY] - B -> B
        B ++
        A <- [CSRY]
        JR chput_esc_ll_loop_end

    chput_esc_ll_loop: /*local*/
        curs2hl()

        DE <-> HL
        A -- -> [CSRY]
        curs2hl()
        chput_copy_line()

    chput_esc_ll_loop_end: /*local*/
        DJNZ chput_esc_ll_loop

        HL -pop                          // ; restore cursor position
           -> [CSRY]
        H <- 0
        A <- [CRTCNT]                    // ; update LINTTB
          -> D                           // ; DE := (CRTCNT)
        E <- 0
        A - L                            // ; BC := (CRTCNT) - (CSRY) - 1
          -- -> C
        B <- 0
        HL <- (LINTTB - 1)               // ; DE := LINTTB + (CRTCNT)
           + DE
        DE <-> HL
        H <- D                           // ; HL := DE - 1
        L <- E
        HL --
        LDDR
        JP chput_esc_k
    }

    // ; -- Delete line (and scroll rest up)
    proc chput_esc_m() {
        chput_ctrl_cr()                  // ; move to start of line

        HL@[CSRY] -push                  // ; save cursor pos
        B <- L
        @[CRTCNT] - B -> B
        B ++
        A <- [CSRY]
        JR chput_esc_m_loop_end

    chput_esc_m_loop: /*local*/
        curs2hl()                        // ;   Copy 1 line:
        DE <-> HL                        // ;     de = dest in VRAM
        A ++                             // ;     next line
          -> [CSRY]
        curs2hl()                        // ;     hl = src in VRAM
        chput_copy_line()

    chput_esc_m_loop_end: /*local*/
        DJNZ chput_esc_m_loop            // ; endloop
        chput_esc_k()                    // ; clear till end of line

        HL -pop                          // ; restore cursor position
           -> [CSRY]
        H <- 0                           // ; update LINTTB
        A <- [CRTCNT]                    // ; BC := (CRTCNT) - (CRSY) - 1
          - L -- -> C
        B <- 0
        DE <- (LINTTB - 1)               // ; DE := LINTTB + (CSRY)
        HL + DE
        D <- H
        E <- L
        HL ++                            // ; HL := DE + 1
        LDIR
        return
    }

    // ; -- Copy line: from HL to DE
    proc chput_copy_line() {
        AF -push
        BC -push
        B <- 0
        @[LINLEN] -> C
        CALL chput_copy_line_copy

        BC -pop
        AF -pop
        return

    chput_copy_line_copy: /*local*/
        HL -push
        DE -push
        BC -push
        DE <- LINWRK
        ldirmv()

        BC -pop
        DE -pop -push
        BC -push
        HL <- LINWRK
        ldirvm()

        BC -pop
        HL -pop + BC
        DE <-> HL
        HL -pop + BC
        return
    }

    // ; -- Erase
    proc chput_erase() {
        @[CSRX] -? 1
        return-if Z?

        A <- 32
        chput_putchar()
        JP chput_ctrl_left
    }

    // ; -- disable cursor
    proc chput_remove_cursor() {
        A <- [CSRSW]                     // ; Cursor visible?
          -? 1
        return-if NZ?

        A <- [SCRMOD]                    // ; Are we in text mode?
          -? 2
        return-if NC?

        A <- [CURSAV]                    // ; get saved character
        curs2hl()                        // ; and drop it at the
        JP wrtvrm
    }

    // ; -- enable cursor
    proc chput_restore_cursor() {
        A <- [CSRSW]                     // ; Cursor visible?
          -? 1
        return-if NZ?

        @[SCRMOD] -? 2
        return-if NC?
        curs2hl()                        // ; get character at cursor
        rdvrm()                          // ; and store at CURSAV

        A -> [CURSAV] & A                // ; reset carry
        D <- 0                           // ; de := 8 * a
        E@A <*$ 1
        D <*$ 1
        E <*$ 1
        D <*$ 1
        E <*$ 1
        D <*$ 1
        A ^ A                            // ; get pattern table address
        HL <- SCRMOD
        A -? [HL]
        JR NZ? chput_restore_cursor_t32

        HL <- [TXTCGP]
        JR chput_restore_cursor_getpattern

    chput_restore_cursor_t32: /*local*/
        HL <- [T32CGP]

    chput_restore_cursor_getpattern: /*local*/
        HL -push + DE                    // ; add offset of character
        DE <- LINWRK                     // ; copy pattern to LINWRK
        BC <- 8
        ldirmv()

        A <- [CSTYLE]                    // ; depending on CSTYLE
          -? 0
        JR NZ? chput_restore_cursor_ins

        HL <- LINWRK                     // ; invert the complete pattern
        B <- 8
        JR chput_restore_cursor_invert

    chput_restore_cursor_ins: /*local*/
        HL <- (LINWRK + 6)               // ; or only the lower 2 lines
        B <- 2

    chput_restore_cursor_invert: /*local*/
        A <- [HL]                        // ; invert!
          -not -> [HL]
        HL ++
        DJNZ chput_restore_cursor_invert

        HL -pop                          // ; copy inverted pattern to
        DE <- (255 * 8)                  // ; pattern 255
        HL + DE
        DE <-> HL
        HL <- LINWRK
        BC <- 8
        ldirvm()
        curs2hl()                        // ; place char 255 at cursor pos

        A <- 255
        JP wrtvrm
    }

    // ; -- Control character search table
    data chput_ctrl_table = byteword [
        { 7 beep }                       // ; chput_ctrl_beep
        { 8 chput_ctrl_bs }
        { 9 chput_ctrl_tab }
        { 10 chput_ctrl_lf }
        { 11 chput_ctrl_home }
        { 12 chput_ctrl_ff }
        { 13 chput_ctrl_cr }
        { 27 chput_ctrl_esc }
        { 28 chput_ctrl_right }
        { 29 chput_ctrl_left }
        { 30 chput_ctrl_up }
        { 31 chput_ctrl_down }
    ]

    // ; -- Escape character search table
    data chput_esc_table = byteword [
        { 'j' chput_esc_j }
        { 'E' chput_esc_e }
        { 'K' chput_esc_k }
        { 'J' chput_esc_jj }
        { 'l' chput_esc_l }
        { 'L' chput_esc_ll }
        { 'M' chput_esc_m }
        { 'Y' chput_esc_yy }
        { 'A' chput_esc_a }
        { 'B' chput_esc_b }
        { 'C' chput_esc_c }
        { 'D' chput_esc_d }
        { 'H' chput_esc_h }
        { 'x' chput_esc_x }
        { 'y' chput_esc_y }
    ]

    // ; vim:ts=8:expandtab:filetype=z8a:syntax=z8a:
    // -- end: chput.asm

    // ;--------------------------------
    // ; $00A5 LPTOUT
    // ; Function : Sends one character to printer
    // ; Input    : A  - ASCII-code of character to send
    // ; Output   : C-flag set if failed
    // ; Registers: F

    proc lptout() {
        CALL H_LPTO

        AF -push

    lptout_wait: /*local*/
        breakx()
        JR C? lptout_abort
        lptstt()
        JR Z? lptout_wait

        AF -pop
        JR lptout_write

    lptout_abort: /*local*/
        A <- 13
        CALL lptout_write

        A ^ A -> [LPTPOS]
        AF -pop
        SCF
        return

    lptout_write: /*local*/
        AF -push
        A -out PRN_DATA <- 0
          -out PRN_STAT -not
          -out PRN_STAT
        AF -pop
        A & A
        return
    }

    // ;--------------------------------
    // ; $00A8 LPTSTT
    // ; Function : Tests printer status
    // ; Output   : A  - #FF and Z-flag reset if printer is ready
    // ;                 #00 and Z-flag set if not ready
    // ; Registers: AF

    proc lptstt() {
        CALL H_LPTS

        A -in PRN_STAT >*$ 2
        A <- 0xff
        JR NC? lptstt_end

        A -not

    lptstt_end: /*local*/
        A & A
        return
    }

    // ;--------------------------------
    // ; $00AB CNVCHR
    // ; Function : tests for the graphic header and transforms the code
    // ; Input    : A  - charactercode
    // ;            GRPHED(FCA6): indicates if previous char was an extension code
    // ; Output:                               C-flag  Z-flag  A
    // ;       if byte is extension byte       low     high    1
    // ;       if byte is normal ASCII         high    low     ASCII code
    // ;       if byte is graphical extension  high    high    extension code
    // ;       GRPHED is updated
    // ; Registers: AF

    proc cnvchr() {
        HL -push
        AF -push
        HL <- GRPHED
        A ^ A -? [HL] -> [HL]            // ; reset GRPHED in advance
        JR NZ? cnvchr_handlegfx

        AF -pop                          // ; we're not in graphic mode
        A -? 1                           // ; graphic header?
        JR NZ? cnvchr_normal

        A -> [HL]                        // ; yes! -> Set GRPHED
        JR cnvchr_normal_exit            // ; we've got NC and Z - perfect!

    cnvchr_handlegfx: /*local*/
        AF -pop
        A -? 0x40
        JR C? cnvchr_nogfx

        A -? 0x60
        JR NC? cnvchr_nogfx

        A - 0x40                         // ; graphic char
          -? A                           // ; set Z (and NC)
        JR cnvchr_normal

    cnvchr_nogfx: /*local*/
        A -? 0x50                        // ; A is definitely not #50
                                         // ; so this sets NZ :-)
    cnvchr_normal: /*local*/
        SCF                              // ; NZ/Z already ok, now set C

    cnvchr_normal_exit: /*local*/
        HL -pop
        return
    }

    // -- begin: inlin.asm

    // ; INLIN/PINLIN/QINLIN routines for C-BIOS
    // ;
    // ; Copyright (c) 2007 Eric Boon.  All rights reserved.
    // ;
    // ; 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.
    // ;
    // ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
    // ;
    // ;--------------------------------
    // ; $00AE PINLIN
    // ; Function : Stores in the specified buffer the character codes input
    // ;           until the return key or STOP key is pressed
    // ; Output   : HL - for the starting address of the buffer -1
    // ;            C-flag set when it ends with the STOP key
    // ; Registers: All
    // ; TODO: call H_PINL
    proc pinlin() {
        CALL H_PINL

        A <- [AUTFLG]                    // ; If AUTO is active
          & A
        JP Z? inlin                      // ; then start line input

        A <- 1                           // ; else set cursor
          -> [CSRX]                      // ;    to left border first
        JP inlin                         // ;    and then start line input
    }

    // ;--------------------------------
    // ; $00B4 QINLIN
    // ; Function : Prints a questionmark and one space and continues with INLIN
    // ; Output   : HL - for the starting address of the buffer -1
    // ;            C-flag set when it ends with the STOP key
    // ; Registers: All
    data qinlin_prompt = byte [
        "? " 0
    ]

    proc qinlin() {
        CALL H_QINL

        HL <- qinlin_prompt
        prn_text()
        fallthrough
    }

    // ; continue with inlin

    // ;--------------------------------
    // ; $00B1 INLIN
    // ; Function : Main line input routine
    // ; Output   : HL - for the starting address of the buffer -1
    // ;            C-flag set when it ends with the STOP key
    // ; Registers: All

    proc inlin() {
        HL <- [CSRX]                     // ; loads CSRX and CSRY
           -> [FSTPOS]                   // ; save in FSTPOS
        DE <- (LINTTB - 2)               // ; break logical line
        H <- 0                           // ; above cursor pos
        A <- L
        HL + DE
        A -> [HL]

    inlin_loop: /*local*/
        chget()                          // ; get a character from the kbd

        A -? 0x7f
        JP Z? inlin_del

        A -? 0x20
        JR NC? inlin_printable

        B <- 20
        HL <- inlin_table
        jump_table()

        A ^ A                            // ; we just put out a ctrl char
          -> [INSFLG]                    // ; switch insert mode off
          -> [CSTYLE]
        JR inlin_loop

    inlin_printable: /*local*/           // ; else...
        AF -push
        @[INSFLG] & A
        CALL NZ? inlin_insert

        AF -pop
        RST 0x18
        JR inlin

    // ; ----------------------------------------------
    inlin_insert: /*local*/
        chput_remove_cursor()

        HL <- [CSRY]                     // ; save cursorpos
           -> [TEMP2]
        A <- ' '                         // ; oldchar = space
          -> [TEMP]

    inlin_insert_loop: /*local*/         // ; REPEAT
        curs2hl()                        // ;   get char under curpos
        rdvrm()

        A -? ' '                         // ;   IF is space
        JR NZ? inlin_insert_cont

        HL <- [CSRY]                     // ;   AND at end of line
        @[LINLEN] -? H
        JR NZ? inlin_insert_cont1

        H <- 0                           // ;   AND logical line does
        DE <- (LINTTB - 1)               // ;     not continue
        HL + DE
        @[HL] | A
        JR Z? inlin_insert_cont1

        A <- [TEMP]                      // ;   THEN
        curs2hl()
        wrtvrm()                         // ;     put old char

        HL <- [TEMP2]                    // ;     restore cursor pos
           -> [CSRY]
        return
        JP chput_restore_cursor          // ;     and exit

    inlin_insert_cont1: /*local*/
        A <- ' '

    inlin_insert_cont: /*local*/
        AF -push                         // ;   ELSE
        A <- [TEMP]                      // ;     put old char
        RST 0x18

        AF -pop
        A -> [TEMP]                      // ;   oldchar = character read
        JR inlin_insert_loop             // ; ENDREP

    // ; ----------------------------------------------
    inlin_wback: /*local*/
        return

    // ; ----------------------------------------------
    inlin_break: /*local*/
        SCF                              // ; C

        HL -pop                          // ; do not return to INLIN
        return                           // ; but to caller of INLIN

    // ; ----------------------------------------------
    inlin_clear: /*local*/
        return

    // ; ----------------------------------------------
    inlin_wfwd: /*local*/
        return

    // ; ----------------------------------------------
    inlin_bs: /*local*/
        return

    // ; ----------------------------------------------
    inlin_cr: /*local*/
        return

    // ; ----------------------------------------------
    inlin_end: /*local*/
        A ^ A                            // ; NZ, NC
        HL -pop                          // ; do not return to INLIN
        return                           // ; but to caller of INLIN

    // ; ----------------------------------------------
    inlin_ins: /*local*/
        return

    // ; ----------------------------------------------
    inlin_clrlin: /*local*/
        return

    // ; -- ESCAPE
    inlin_esc: /*local*/
        return                           // ; Do nothing

    // ; -- DELETE
    inlin_del: /*local*/
        return

        // ; -- Jump table. Control chars not handled in one of the routines above
        // ;    are simply forwarded to OUTDO
        data inlin_table = word [ /*local*/
            0x18                         // ; @
            0x18                         // ; A -
            inlin_wback                  // ; B word back
            inlin_break                  // ; C stop, abort, quit
            0x18                         // ; D
            inlin_clear                  // ; E: clear to end of line
            inlin_wfwd                   // ; F: word fwd
            0x18                         // ; G
            inlin_bs                     // ; H BACKSP: erase char left
            0x18                         // ; I
            0x18                         // ; J
            0x18                         // ; K
            0x18                         // ; L
            inlin_cr                     // ; M ENTER : confirm, yes, ok
            inlin_end                    // ; N to end of line
            0x18                         // ; O
            0x18                         // ; P
            0x18                         // ; Q
            inlin_ins                    // ; R INSERT: toggle insert mode
            0x18                         // ; S
            0x18                         // ; T
            inlin_clrlin                 // ; U clear line
            0x18                         // ; V
            0x18                         // ; W
            0x18                         // ; X
            0x18                         // ; Y
            0x18                         // ; Z
            inlin_esc                    // ; ESCAPE: ignore
            0x18                         // ; (28)
            0x18                         // ; (29)
            0x18                         // ; (30)
            0x18                         // ; (31)
        ]
    }

    // ; vim:ts=8:expandtab:filetype=z8a:syntax=z8a:
    // -- end: inlin.asm

    // ;--------------------------------
    // ; $00B7 BREAKX
    // ; Tests status of CTRL-STOP.
    // ; This routine reads the keyboard status from the hardware, so its result
    // ; will be accurate even if interrupts have been disabled for a while.
    // ; Output:  CF set if CTRL-STOP is pressed
    // ; Changes: AF
    proc breakx() {
        A -in GIO_REGS & 0xf0 | 0x07
          -out GIO_REGS
        A -in KBD_STAT & 0x10            // ; check STOP, also resets CF
        return-if NZ?                    // ; some programs like to return with $10

        A -in GIO_REGS & 0xf0 | 0x06
          -out GIO_REGS
        A -in KBD_STAT & 0x02            // ; check CTRL, also resets CF
        return-if NZ?
        SCF
        return
    }

    // ;--------------------------------
    // ; $00BA ISCNTC
    // ; Function: Test status of STOP or CTRL-STOP; if BASIC is in a ROM (see BASROM),
    // ;           then check for STOP or CTRL-STOP is not done. Otherways:
    // ;       INTLFLG: 0 => no action
    // ;       INTLFLG: 3 => CTRL-STOP pressed => break program, if "STOP-interrupts not on"??
    // ;       INTLFLG: 4 => STOP pressed => wait in ISCNTC till stop pressed again
    // ; Input: INTFLG, BASROM
    // ; Registers: AF
    // ; NOTE: this implementation is still a stub!
    proc iscntc() {
        HL -push
        AF -push
        HL <- iscntc_text
        print_debug()

        AF -pop
        HL -pop
        return

        data iscntc_text = byte [ /*local*/
            "ISCNTC" 0
        ]
    }

    // ;--------------------------------
    // ; $00BD CKCNTC
    // ; Function : Same as ISCNTC. used in Basic
    proc ckcntc() {
        JP iscntc
    }

    // ;--------------------------------
    // ; $00C0 BEEP
    // ; Function : play a short beep, and reset sound system via GICINI
    // ; Registers: All
    // ; NOTE: this implementation is still a stub!
    proc beep() {
        // ; Note: Called by CHPUT; if you need to change more regs than AF, HL, DE, BC
        // ;       then update CHPUT.
        HL -push
        AF -push
        HL <- beep_text
        print_debug()

        AF -pop
        HL -pop
        return

        data beep_text = byte [ /*local*/
            "BEEP" 0
        ]
    }

    // ;--------------------------------
    // ; $00C6 POSIT
    // ; Sets cursor position.
    // ; Input:   H = column
    // ;          L = row
    // ; Changes: AF
    proc posit() {
        // ; Note: this works because CSRX == CSRY + 1
        HL -> [CSRY]
        return
    }

    // ;--------------------------------
    // ; $00C9 FNKSB
    // ; Tests whether the function key display is active (FNKFLG),
    // ; if so, displays them, otherwise erases them.
    // ; Input:   FNKFLG (#FBCE)
    // ; Changes: all
    // ; NOTE: This implementation is still a stub!
    proc fnksb() {
        HL -push
        AF -push
        HL <- fnksb_text
        print_debug()

        AF -pop
        HL -pop
        return

        data fnksb_text = byte [ /*local*/
            "FNKSB" 0
        ]
    }

    // ;--------------------------------
    // ; $00CC ERAFNK
    // ; Erase function key display.
    // ; Changes: all
    // ; NOTE: This implementation is still a stub!
    // ; TODO: call H_ERAF
    proc erafnk() {
        // ;               call    H_ERAF
        HL -push
        AF -push
        HL <- erafnk_text
        print_debug()

        AF -pop
        HL -pop
        return

        data erafnk_text = byte [ /*local*/
            "ERAFNK" 0
        ]
    }

    // ;--------------------------------
    // ; $00CF DSPFNK
    // ; Display function keys.
    // ; Changes: all
    // ; NOTE: This implementation is still a stub!
    // ; TODO: call H_DSPF
    proc dspfnk() {
        // ;               call    H_DSPF
        HL -push
        AF -push
        HL <- dspfnk_text
        print_debug()

        AF -pop
        HL -pop
        return

        data dspfnk_text = byte [ /*local*/
            "DSPFNK" 0
        ]
    }

    // ;--------------------------------
    // ; $00D2 TOTEXT
    // ; Forces the screen to be in the text mode.
    // ; Input: SCRMOD, OLDSCR
    // ; Changes: all
    proc totext() {
        @[SCRMOD] -? 2
        return-if C?

        A <- [OLDSCR]
        CALL H_TOTE

        A | A
        JP Z? initxt
        JP init32
    }

    // ;--------------------------------
    // ; $00E1 TAPION
    // ; Reads the header block after turning the cassette motor on.
    // ; Output:  CF = set if failed
    // ; Changes: all
    // ; NOTE: This implementation is still a stub!
    proc tapion() {
        HL -push
        AF -push
        HL <- tapion_text
        print_debug()

        AF -pop
        HL -pop

        // ; TODO: not implemented -> always fail
        SCF
        return

        data tapion_text = byte [ /*local*/
            "TAPION" 0
        ]
    }

    // ;--------------------------------
    // ; $00E4 TAPIN
    // ; Read data from the tape.
    // ; Output:  A = data read
    // ; Changes: all
    // ; NOTE: This implementation is still a stub!
    proc tapin() {
        HL -push
        AF -push
        HL <- tapin_text
        print_debug()

        AF -pop
        HL -pop

        // ; TODO: not implemented -> always fail
        SCF
        return

        data tapin_text = byte [ /*local*/
            "TAPIN" 0
        ]
    }

    // ;--------------------------------
    // ; $00E7 TAPIOF
    // ; Stops reading from the tape.
    // ; NOTE: This implementation is still a stub!
    proc tapiof() {
        HL -push
        AF -push
        HL <- tapiof_text
        print_debug()

        AF -pop
        HL -pop
        return

        data tapiof_text = byte [ /*local*/
            "TAPIOF" 0
        ]
    }

    // ;--------------------------------
    // ; $00EA TAPOON
    // ; Turns on the cassette motor and writes the header.
    // ; Input:   A  = zero for short header, non-zero for long header
    // ; Output:  CF = set if failed
    // ; Changes: all
    // ; NOTE: This implementation is still a stub!
    proc tapoon() {
        HL -push
        AF -push
        HL <- tapoon_text
        print_debug()

        AF -pop
        HL -pop

        // ; TODO: not implemented -> always fail
        SCF
        return

        data tapoon_text = byte [ /*local*/
            "TAPOON" 0
        ]
    }

    // ;--------------------------------
    // ; $00ED TAPOUT
    // ; Writes data to the tape.
    // ; Input:   A  = data to write
    // ; Output:  CF = set if failed
    // ; Changes: all
    // ; NOTE: This implementation is still a stub!
    proc tapout() {
        HL -push
        AF -push
        HL <- tapout_text
        print_debug()

        AF -pop
        HL -pop

        // ; TODO: not implemented -> always fail
        SCF
        return

        data tapout_text = byte [ /*local*/
            "TAPOUT" 0
        ]
    }

    // ;--------------------------------
    // ; $00F0 TAPOOF
    // ; Stops writing on the tape.
    // ; NOTE: This implementation is still a stub!
    proc tapoof() {
        HL -push
        AF -push
        HL <- tapoof_text
        print_debug()

        AF -pop
        HL -pop
        return

        data tapoof_text = byte [ /*local*/
            "TAPOOF" 0
        ]
    }

    // ;--------------------------------
    // ; $00F3 STMOTR
    // ; Changes the cassette motor state.
    // ; Input:   A = action: #00 stops motor, #01 starts motor,
    // ;                      #FF inverts current state
    // ; Changes: AF
    proc stmotr() {
        BC -push
        B <- A
        A -in GIO_REGS
        B ++
        JR Z? stmotr_inv

        A -set 4
        B --
        JR Z? stmotr_set

        A -reset 4
        B --
        JR Z? stmotr_set

        BC -pop
        return

    stmotr_inv: /*local*/
        A ^ 16

    stmotr_set: /*local*/
        A -out GIO_REGS
        BC -pop
        return
    }

    // ;--------------------------------
    // ; $0090 GICINI  Initialize Sound IC
    // ; Function : Initialises PSG and sets initial value for the PLAY statement
    // ; Registers: All
    proc gicini() {
        E <- 0x00
        A <- 0x08
        wrtpsg()

        A ++
        wrtpsg()

        A ++
        wrtpsg()

        A ++
        E <- 0xb8
        A <- 0x07
        wrtpsg()
        return
    }

    // ;--------------------------------
    // ; $0093 WRTPSG
    // ; Function : Writes data to PSG-register
    // ; Input    : A  - PSG register number
    // ;            E  - data write
    proc wrtpsg() {
        DI

        A -out PSG_REGS
        AF -push
        @E -out PSG_DATA
        EI

        AF -pop
        return
    }

    // ;--------------------------------
    // ; $0096 RDPSG
    // ; Function : Reads value from PSG-register
    // ; Input    : A  - PSG-register read
    // ; Output   : A  - value read
    proc rdpsg() {
        A -out PSG_REGS -in PSG_STAT
        return
    }

    // ;--------------------------------
    // ; $0135 CHGSND
    // ; Write to the 1-bit sound port.
    // ; Input:   A = zero to set sound state to 0, non-zero to set sound state to 1
    // ; Changes: AF
    proc chgsnd() {
        A | A <- 0x0e                    // ; $0E = command to reset bit 7
        JR Z? chgsnd_write

        A ++                             // ; $0F = command to set bit 7

    chgsnd_write: /*local*/
        A -out PPI_REGS                  // ; set/reset bit of port C
        return
    }

    // ;--------------------------------
    // ; $0138 RSLREG
    // ; Function : Reads the primary slot register
    // ; Output   : A  - for the value which was read
    // ;            33221100
    // ;            ||||||- Pagina 0 (#0000-#3FFF)
    // ;            ||||--- Pagina 1 (#4000-#7FFF)
    // ;            ||----- Pagina 2 (#8000-#BFFF)
    // ;            ------- Pagina 3 (#C000-#FFFF)
    // ; Registers: A
    proc rslreg() {
        A -in PSL_STAT
        return
    }

    // ;--------------------------------
    // ; $013B WSLREG
    // ; Function : Writes value to the primary slot register
    // ; Input    : A  - value value to (see RSLREG)
    proc wslreg() {
        A -out PSL_STAT
        return
    }

    // ;--------------------------------
    // ; $013E RDVDP
    // ; Function : Reads VDP status register
    // ; Output   : A  - Value which was read
    // ; Registers: A
    proc rdvdp() {
        A -in VDP_STAT
        return
    }

    // ;--------------------------------
    // ;0141h SNSMAT
    // ; Function : Returns the value of the specified line from the keyboard matrix
    // ; Input    : A  - for the specified line
    // ; Output   : A  - for data (the bit corresponding to the pressed key will be 0)
    // ; Registers: AF
    proc snsmat() {
        DI

        BC -push
        C <- A
        A -in GIO_REGS & 0xf0 | C
          -out GIO_REGS
        A -in KBD_STAT
        BC -pop
        EI
        return
    }

    // ;--------------------------------
    // ; $0144 PHYDIO
    // ; Executes I/O for mass-storage media like diskettes.
    // ; All this routine does is call H_PHYD, which should be installed by the main
    // ; disk ROM.
    // ; Input:     B  = number of sectors to save/load
    // ;            C  = media ID of the disk
    // ;            DE = begin sector
    // ;            HL = begin address in memory
    // ; Changes:   all
    // ; Remark:    Before the call is called, the Z-flag must be reset, and the
    // ;            execution address which was in HL must be at the last stack address
    proc phydio() {
        CALL H_PHYD
        return
    }

    // ;--------------------------------
    // ; $0147 FORMAT
    // ; Initialises mass-storage media like formatting of diskettes.
    // ; All this routine does is call H_FORM, which should be installed by the main
    // ; disk ROM.
    // ; Changes:   all
    proc format() {
        CALL H_FORM
        return
    }

    // ;--------------------------------
    // ; $014A ISFLIO
    // ; Function : Tests if I/O to device is taking place
    // ; Output   : A  - #00 if not taking place
    // ;             not #00 if taking place
    // ; Registers: AF
    // ; TODO: call H_ISFL
    proc isflio() {
        // ;                call    H_ISFL
        @[PTRFIL] & A                    // ; adjust flags
        return
    }

    // ;--------------------------------
    // ; $00D5 GTSTCK
    // ; Function : Returns the joystick status
    // ; Input    : A  - Joystick number to test (0 = cursors, 1 = port 1, 2 = port 2)
    // ; Output   : A  - Direction,D = $00(when A=0)
    // ; Registers: All
    proc gtstck() {
        BC -push
        A -? 0x00
        JR NZ? joy_stc1

        A <- 0x08
        snsmat()

        A >* 4 -not & 0x0f               // ; 0000RDUL
        HL -push <- joypos_kbd_tbl
        D <- 0
        E <- A
        HL + DE
        A <- [HL]
        HL -pop
        BC -pop
        A & A
        return

    joy_stc1: /*local*/
        // ;PSG reg 15h
        // ;0J001111
        // ;PSG reg 14h
        // ;00BARLDU
        HL -push
        DE -push
        E <- 0x00
        A --
        JR Z? sel_stc1

        E -set 6                         // ; select stick 2

    sel_stc1: /*local*/
        A <- 0x0f
        DI
        rdpsg()
        EI

        A & 0xbf | E -> E
        A <- 0x0f
        wrtpsg()

        A <- 0x0e
        DI
        rdpsg()
        EI

        A -not & 0x0f                    // ; 0000RLDU
        HL <- joypos_joy_tbl
        B <- 0
        C <- A
        HL + BC
        A <- [HL]
        DE -pop
        HL -pop
        BC -pop
        A & A
        return
    }

    proc joy_end() { /*unused*/
        A <- 0x00
        BC -pop
        A & A
        return
    }

    data joypos_joy_tbl = byte [
        // ;         0   1   2   3   4   5   6   7
        0x00 0x01 0x05 0x00 0x07 0x08 0x06 0x07

        // ;         8   9   A   B   C   D   E   F
        0x03 0x02 0x04 0x03 0x00 0x01 0x05 0x00
    ]

    data joypos_kbd_tbl = byte [
        // ;         0   1   2   3   4   5   6   7
        0x00 0x07 0x01 0x08 0x05 0x06 0x00 0x07

        // ;         8   9   A   B   C   D   E   F
        0x03 0x00 0x02 0x01 0x04 0x05 0x03 0x00
    ]

    // ;--------------------------------
    // ; $00D8 GTTRIG
    // ; Function : Returns current trigger status
    // ; Input    : A  - trigger button to test
    // ;            0 = spacebar(included A-1 = minus)
    // ;            1 = port 1, button A
    // ;            2 = port 2, button A
    // ;            3 = port 1, button B
    // ;            4 = port 2, button B
    // ; Output   : A  - #00 trigger button not pressed
    // ;                 #FF trigger button pressed
    // ; Note     : Some programs rely on ZF to be set according to the value in A.
    // ; Registers: All
    proc gttrig() {
        A -? 5
        JR NC? gttrig_space              // ; if value of A is above 5,go space routine

        A | A
        JR NZ? joy_trig

    gttrig_space: /*local*/
        // ; Keyboard (spacebar)
        A <- 0x08
        snsmat()                         // ; bit0 = 0 -> space pressed

        A | 0xfe                         // ; FE -> pressed, FF -> not pressed
          ++                             // ; FF -> pressed, 00 -> not pressed
        return

    // ; Joystick triggers
    joy_trig: /*local*/
        DI

        A --
        DE -push
        E <- 0x03                        // ; enable trig A+B of stick 1
        B <- A
        A & 0x01
        JR Z? sel_trig1

        E <- 0x4c                        // ; enable trig A+B of stick 2 and select stick 2

    sel_trig1: /*local*/
        A <- 0x0f
        rdpsg()

        A & 0xbf | E -> E
        A <- 0x0f
        wrtpsg()

        A <- B
        B <- 0x10
        A & 0x02
        JR Z? istrg_a

        B <- 0x20

    istrg_a: /*local*/
        A <- 0x0e
        DI
        rdpsg()
        EI

        DE -pop
        A & B
        JR Z? trig_on
        JR trig_off

    trig_on: /*local*/
        A <- 0xff
        return

    trig_off: /*local*/
        A ^ A
        return
    }

    // ;--------------------------------
    // ; $00DB GTPAD
    // ; Function : Returns current touch pad status
    // ; Input    : A  - Touchpad number to test
    // ; Output   : A  - Value
    // ; Registers: All
    // ; NOTE     : This implementation is still a stub!
    proc gtpad() {
        HL -push
        AF -push
        HL <- gtpad_text
        print_debug()

        AF -pop
        HL -pop
        A ^ A                            // ; haywire
        return

        data gtpad_text = byte [ /*local*/
            "GTPAD" 0
        ]
    }

    // ;--------------------------------
    // ; $00DE GTPDL
    // ; Function : Returns currenct value of paddle
    // ; Input    : A  - Paddle number
    // ; Output   : A  - Value
    // ; Registers: All
    // ; NOTE     : This implementation is still a stub!
    proc gtpdl() {
        HL -push
        AF -push
        HL <- gtpdl_text
        print_debug()

        AF -pop
        HL -pop
        return

        data gtpdl_text = byte [ /*local*/
            "GTPDL" 0
        ]
    }

    // ;--------------------------------
    // ; $00F6 LFTQ
    // ; Give the number of bytes left in a queue.
    // ; In:      A  = queue number
    // ; Out:     HL = number of bytes left
    // ; Changes: AF, BC, HL
    proc lftq() {
        calc_queue_address()

        B <- [HL]                        // ; B = put position
          ++
        HL ++
        A <- [HL]                        // ; A = get position
          - B                            // ; (getpos - putpos) & size
        HL ++ ++
        A & [HL] -> L
        H <- 0x00                        // ; Queues are smaller than 256 bytes.
        return
    }

    // ;--------------------------------
    // ; $00F9 PUTQ
    // ; Put a byte in a queue.
    // ; In:      A  = queue number
    // ;          E  = data
    // ; Out:     ZF = set if the queue is full
    // ; Changes: AF, BC, HL
    proc putq() {
        // ; Check whether the queue is full.
        calc_queue_address()

        @[HL] ++ -> B                    // ; B = put position + 1
        HL ++
        A - [HL]
        return-if Z?

        // ; Save the new put position.
        A <- B
        HL ++ ++ -push
        A & [HL]                         // ; (putpos + 1) & size
        HL -- -- --
        A -> [HL]

        // ; Put the data byte in the queue.
        HL -pop ++
        A <- [HL]                        // ; Get the buffer address.
        HL ++
        H <- [HL]
        L <- A
        B --                             // ; Add putpos.
          -> C
        B <- 0
        HL + BC
        E -> [HL]
        A | 1
        return
    }

    // ; Calculate the address to the start of queue control block.
    // ; A = queue number
    proc calc_queue_address() {
        HL <- [QUEUES]                   // ; See QUETAB in systemvars.asm.
        B <- A                           // ; (queue number * 6)
        A <* 2 + B + B -> C
        B <- 0
        HL + BC
        return
    }

    // ;--------------------------------
    // ; $0132 CHGCAP
    // ; Function : Alternates the CAP lamp status
    // ; Input    : A  - #00 is lamp on
    // ;             not #00 is lamp off
    // ; Registers: AF
    proc chgcap() {
        A | A -in GIO_REGS -reset 6
        JR NZ? chgcap_on

        A -set 6

    chgcap_on: /*local*/
        A -out GIO_REGS
        return
    }

    // ;--------------------------------
    // ; $014D OUTDLP
    // ; Function : Printer output
    // ; Input    : A  - code to print
    // ; Registers: F
    // ; Remark   : Differences with LPTOUT:
    // ;            1. TAB is expanded to spaces
    // ;            2. For non-MSX printers, Hiragana is transformed to katakana
    // ;               and graphic characters are transformed to 1-byte characters
    // ;            3. If failed, device I/O error occurs
    // ; TODO     : This implementation is still a stub!
    proc outdlp() {
        HL -push
        AF -push
        HL <- outdlp_text
        print_debug()

        AF -pop
        HL -pop
        return

        data outdlp_text = byte [ /*local*/
            "OUTDLP" 0
        ]
    }

    // ;--------------------------------
    // ; $0150 GETVCP
    // ; Returns pointer to a variable at offset 2 in a voice structure.
    // ; TODO: find out the purpose of this variable.
    // ; Address  : #0150
    // ; Function : Returns pointer to play queue
    // ; Input    : A  - Channel number
    // ; Output   : HL - Pointer
    // ; Registers: AF
    // ; Remark   : Only used to play music in background
    proc getvcp() {
        L <- 2
        JR getvc2_a
    }

    // ;--------------------------------
    // ; $0153 GETVC2
    // ; Returns pointer to a given variable in a voice structure.
    // ; Input    : L        - Pointer in play buffer
    // ;            (VOICEN) - Voice structure number
    // ; Output   : HL - Pointer
    // ; Registers: AF
    proc getvc2() {
        A <- [VOICEN]
        fallthrough
    }

    proc getvc2_a() {
        DE -push
        D <- 0
        E <- L
        HL@VCBA + DE
        E <- 37                          // ; Size of a structure

    getvc2_loop: /*local*/
        A | A
        JR Z? getvc2_exit

        HL + DE
        A --
        JR getvc2_loop

    getvc2_exit: /*local*/
        DE -pop
        return
    }

    // ;--------------------------------
    // ; $0156 KILBUF
    // ; Empties the keyboard buffer.
    // ; Changes: HL
    proc kilbuf() {
        HL@[GETPNT] -> [PUTPNT]
        return
    }

    // ;--------------------------------
    // ; Interrupt routine ($0038h)
    // ;--------------------------------
    // ; some games uses Reg.R and the routine affects the register's value.
    // ; if you want to add something to the routine,please try the following first
    // ;
    // ; Riseout , Replicart

    proc keyint() {
        HL -push
        DE -push
        BC -push
        AF -push
        EXX

        AF <-> AF-
        HL -push
        DE -push
        BC -push
        AF -push
        IY -push
        IX -push
        CALL H_KEYI

        A -in VDP_STAT | A -> [STATFL]   // ; save status
        JP P? int_end                    // ; exit if this is not the VDP int
        CALL H_TIMI

        // ; TODO: (BASIC related stuff)
        // ;       Check sprite collision
        // ;       Update INTCNT

        HL@[JIFFY] ++ -> [JIFFY]

        // ; TODO: MUSICF

        // ; TODO: It seems unsafe to me to already allow interrupts
        // ;       while this one is still busy: possible interference
        // ;       between two interrupts and also the amount of stack
        // ;       space claimed is a lot.
        // ;ei

        // ; Riseout needs that count of RegR in the routine is not
        // ; even number
        // ; nop

        A ^ A -> [CLIKFL]

        // ; Scan the keyboard every three interrupts.
        @[SCNCNT] -- -> [SCNCNT]
        JR NZ? int_end

        @3 -> [SCNCNT]

        // ; TODO read joystick triggers and space for TRGFLG
        A ^ A
        gttrig()

        A -not & 0x01 -> [TRGFLG]
        key_in()

        // ; Check whether KEYBUF is empty and if so, decrement REPCNT to
        // ; see if auto-repeating should be started.  The user program
        // ; needs to continuously read characters to allow repetition.
        HL <- [PUTPNT]
        DE <- [GETPNT]
        RST 0x20
        JR NZ? int_end

        @[REPCNT] -- -> [REPCNT]
        JR NZ? int_end

        HL <- OLDKEY
        BC <- 0x0bff

    clear_oldkey: /*local*/
        C -> [HL]
        HL ++
        DJNZ clear_oldkey
        key_in()

        @1 -> [REPCNT]

    int_end: /*local*/
        IX -pop
        IY -pop
        AF -pop
        BC -pop
        DE -pop
        HL -pop
        EXX

        AF <-> AF- -pop
        BC -pop
        DE -pop
        HL -pop
        EI
        return
    }

    // ;--------------------------------
    // ; 0066h NMI interrupt
    proc nmi() {
        CALL H_NMI
        RETN
    }

    // ;--------------------------------
    // ; Get buffer from keyboard input
    proc key_in() {
        A -in GIO_REGS & 0xf0 -> C
        B <- 11
        HL <- NEWKEY

    key_in_lp: /*local*/
        @C -out GIO_REGS
        A -in KBD_STAT -> [HL]
        HL ++
        C ++
        DJNZ key_in_lp

        IX <- OLDKEY
        DE <- NEWKEY

        // ; Use plain or SHIFT version of rows 0-5?
        // ; Note that while we have tables for GRAPH and CODE variants,
        // ; those are not used yet by this routine.
        @[(NEWKEY + 6)] >* 1
        HL <- scode_tbl
        JR C? scan_start

        HL <- scode_tbl_shift

    scan_start: /*local*/
        C <- 11

    key_chk_lp: /*local*/
        @[DE] -? [IX 0]
        CALL NZ? key_set_delay

        A -not & [IX 0]
        AF <-> AF-                       // ; Update OLDKEY.
        @[DE] -> [IX 0]
        AF <-> AF-

        // ; TODO: Optimise scanning if no keys are pressed.
        // ;       That's the most common case by far.
        B <- 0x08

    key_bit_lp: /*local*/
        A >* 1
        JR C? key_store

    key_bit_next: /*local*/
        HL ++
        DJNZ key_bit_lp

        IX ++
        DE ++
        C --
        return-if Z?

        @C -? 5
        JR NZ? key_chk_lp

        // ; Switch to new table for rows 6-11.
        // ; These rows produce the same characters regardless of which
        // ; modifier keys are held.
        HL <- scode_tbl_otherkeys
        JR key_chk_lp

    key_set_delay: /*local*/
        // ; Set the auto-repeat delay.
        AF -push
        @5 -> [REPCNT]
        AF -pop
        return

    key_store: /*local*/
        AF -push
        @C -? 0x05
        JR Z? key_chk_fnk1

        A -? 0x04
        JR Z? key_chk_fnk2
        JP key_ascii

    // ; Put function string into buffer
    key_chk_fnk1: /*local*/
        // ; F1-F3
        @B -? 0x03                       // ; F1
        JR NZ? key_chk_f2

        A <- 0x00
        JR put_key_fnk

    key_chk_f2: /*local*/
        A -? 0x02                        // ; F2
        JR NZ? key_chk_f3

        A <- 0x01
        JR put_key_fnk

    key_chk_f3: /*local*/
        A -? 0x01                        // ; F3
        JR NZ? key_ascii                 // ; return to normal process

        A <- 0x02
        JR put_key_fnk

    key_chk_fnk2: /*local*/
        // ; F4-F5
        @B -? 0x08                       // ; F4
        JR NZ? key_chk_f5

        A <- 0x03
        JR put_key_fnk

    key_chk_f5: /*local*/
        A -? 0x07                        // ; F5
        JR NZ? key_ascii

        A <- 0x04
        JR put_key_fnk

    put_key_fnk: /*local*/
        HL -push
        BC -push
        DE -push
        A <* 4
        HL <- FNKSTR
        D <- 0x00
        E <- A
        HL + DE
        DE <-> HL

    put_key_fnk_lp: /*local*/
        @[DE] & A
        JR Z? put_key_fnk_nul

        DE -push
        CALL key_put_into_buf

        DE -pop ++
        JR put_key_fnk_lp

    put_key_fnk_nul: /*local*/
        DE -pop
        BC -pop
        HL -pop
        JR key_store_end2

    // ; Check scan table
    key_ascii: /*local*/
        A <- [HL]                        // ; get ASCII value
          & A                            // ; dead key?
        JR Z? key_store_end2

        // ; Store ASCII value in key buffer.
        // ; Since a full buffer is indicated by PUTPNT == GETPNT - 1,
        // ; it is always safe to store a character, but if the buffer
        // ; is full, PUTPNT cannot be increased.

        HL -push
        CALL key_put_into_buf

        HL -pop

    key_store_end2: /*local*/
        AF -pop
        JP key_bit_next

    // ;--------------------------------
    key_put_into_buf: /*local*/
        HL <- [PUTPNT]
        A -> [HL]

        // ; Note: Ashguine 2 has a bug: it puts KEYBUF at FDF0 iso FBF0
        // ;       in the name input routine. This writes keys in memory
        // ;       reserved for hooks, but since those hooks are only used
        // ;       by BASIC, the game doesn't suffer. When PUTPNT reaches
        // ;       FE18, it wraps back to FBF0.
        HL ++
        A <- L

        // ;                cp      $00FF & (KEYBUF + 40)
        A -? 0x18
        JR NZ? key_store_nowrap

        HL <- KEYBUF

    key_store_nowrap: /*local*/
        // ; Check whether the buffer is full.
        DE -push <- [GETPNT]
        RST 0x20

        DE -pop
        return-if Z?

        HL -> [PUTPNT]
        return
    }

    // ;--------------------------------
    // ; $015C SUBROM
    // ; Function : Calls a routine in SUB-ROM
    // ; Input    : IX - Address of routine in SUB-ROM
    // ; Output   : Depends on the routine
    // ; Registers: Alternative registers, IY
    // ; Remark   : Use of EXTROM or CALSLT is more convenient.
    // ;            You have to use this routine like this:
    // ;               push    ix
    // ;               jp      subrom
    // ;            The purpose is unclear
    proc subrom() { /*unused*/
        extrom()

        IX -pop
        return
    }

    // ;--------------------------------
    // ; $015F EXTROM
    // ; Function : Calls a routine in SUB-ROM. Most common way
    // ; Input    : IX - Address of routine in SUB-ROM
    // ; Output   : Depends on the routine
    // ; Registers: Alternative registers, IY
    // ; Remark   : Use: LD IX,address
    // ;                 CALL EXTROM
    proc extrom() {
        // ; EXTROM needs to save alternative registers
        // ; and when call with certain status, returns with EI
        AF <-> AF-
        EXX

        AF -push
        BC -push
        DE -push
        HL -push
        A <- I
        AF -push
        EXX

        IY -push
        A <- [EXBRSA]
        AF -push
        IY -pop                          // ; IYH = slot ID
        AF <-> AF-
        calslt()                         // ; Perform inter-slot call.

        IY -pop
        AF <-> AF-
        EXX

        AF -pop
        JP PO? extrom_skip_ei
        EI

    extrom_skip_ei: /*local*/
        HL -pop
        DE -pop
        BC -pop
        AF -pop
        EXX

        AF <-> AF-
        return
    }

    // ;------------------------------------
    proc hang_up_mode() {
        JR __PC__
    }

    // ;------------------------------------
    // ; Called if the stack underflows.
    proc stack_error() { /*unused*/
        CALL H_STKE

        DE <- str_stack_error
        JP print_error
    }

    // ;------------------------------------
    // ; $0159 CALBAS
    // ; Function : Executes inter-slot call to the routine in BASIC interpreter
    // ; Input    : IX - for the calling address
    // ; Output   : Depends on the called routine
    // ; Registers: Depends on the called routine
    proc calbas() {
        HL -push
        AF -push
        HL <- calbas_text
        print_debug()

        AF -pop
        HL -pop
        DE <- str_no_basic_intr
        JP print_error

        data calbas_text = byte [ /*local*/
            "CALBAS" 0
        ]
    }

    // ;------------------------------------
    // ;Display error
    // ;in DE= message address

    proc print_error() {
        A -in VDP_STAT                   // ; reset Latch
        HL <- vdp_bios
        B <- 0x0c
        C <- VDP_ADDR
        OTIR

        BC <- 0x0800

    lp_clearmem: /*local*/
        A ^ A -out VDP_DATA
        BC --
        @B | C
        JR NZ? lp_clearmem

        HL <- B_Font
        BC <- 0x0800

    lp_fontset: /*local*/
        @[HL] -out VDP_DATA
        HL ++
        BC --
        @B | C
        JR NZ? lp_fontset

        // ;set cursor to (0,0)
        @0x00 -out VDP_ADDR
        @0x40 -out VDP_ADDR
        HL <- str_error_prompt
        A <- [HL]

    lp_errprn: /*local*/
        A -out VDP_DATA
        HL ++
        @[HL] & A
        JR NZ? lp_errprn

        A <- [DE]

    lp_strprn: /*local*/
        A -out VDP_DATA
        DE ++
        @[DE] & A
        JR NZ? lp_strprn
        JP hang_up_mode
        org> 0x1bbf
    }

    // -- begin: font.asm

    // ; Font data for C-BIOS.
    // ;
    // ; Copyright (c) 2010 Manuel Bilderbeek.  All rights reserved.
    // ;
    // ; 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.
    // ;
    // ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.

    B_Font:
    // -- begin: font_jp.asm
    data byte [
        // ; JP version font designed by BouKiCHi.
        // ;
        // ; Copyright (c) 2002-2005 BouKiCHi.  All rights reserved.
        // ;
        // ; 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.
        // ;
        // ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.

        // ; Japanese Localized font
        0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 // ;1
        0x00 0x7c 0x44 0x7c 0x44 0x7c 0x44 0x8c // ;2
        0x00 0x20 0xa4 0xa4 0x20 0x30 0x48 0x84 // ;3
        0x00 0x12 0x14 0xf8 0x38 0x54 0x92 0x10 // ;4
        0x00 0x10 0x7c 0x10 0x38 0x54 0x92 0x10 // ;5
        0x00 0x38 0x54 0xba 0x10 0x38 0x54 0xfe // ;6
        0x00 0x10 0x10 0x7c 0x10 0x10 0x10 0xfe // ;7
        0x00 0x7c 0x44 0x44 0x7c 0x44 0x44 0x7c // ;8
        0x00 0x40 0x7c 0x90 0x7c 0x50 0xfc 0x10 // ;9
        0x00 0xfc 0xa4 0xa4 0xfc 0x84 0x84 0x84 // ;10
        0x00 0x04 0xee 0xa4 0xfe 0xac 0xe4 0x0c // ;11
        0x00 0x28 0x44 0x82 0x7c 0x24 0x44 0x48 // ;12
        0x00 0x24 0xce 0x55 0xec 0x62 0xd2 0x44 // ;13
        0x00 0x7c 0x20 0x7c 0x44 0x7c 0x44 0x7c // ;14
        0x00 0x1c 0x70 0x10 0xfe 0x10 0x10 0x10 // ;15
        0x00 0xfc 0x40 0x40 0x7c 0x44 0x84 0x88 // ;16
        0x00 0x00 0x7c 0x28 0x28 0x48 0x48 0x8c // ;17
        0x10 0x10 0x10 0x10 0xff 0x00 0x00 0x00 // ;18
        0x00 0x00 0x00 0x00 0xff 0x10 0x10 0x10 // ;19
        0x10 0x10 0x10 0x10 0xf0 0x10 0x10 0x10 // ;20
        0x10 0x10 0x10 0x10 0x1f 0x10 0x10 0x10 // ;21
        0x10 0x10 0x10 0x10 0xff 0x10 0x10 0x10 // ;22
        0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 // ;23
        0x00 0x00 0x00 0x00 0xff 0x00 0x00 0x00 // ;24
        0x00 0x00 0x00 0x00 0x1f 0x10 0x10 0x10 // ;25
        0x00 0x00 0x00 0x00 0xf0 0x10 0x10 0x10 // ;26
        0x10 0x10 0x10 0x10 0x1f 0x00 0x00 0x00 // ;27
        0x10 0x10 0x10 0x10 0xf0 0x00 0x00 0x00 // ;28
        0x81 0x42 0x24 0x18 0x18 0x24 0x42 0x81 // ;29
        0x00 0x10 0x7c 0x10 0x10 0x28 0x48 0x84 // ;30
        0x00 0x10 0x7c 0x54 0x7c 0x10 0x10 0x10 // ;31
        0x00 0x10 0x10 0x54 0x54 0x94 0x10 0x30 // ;32
        0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 // ;33
        0x00 0x18 0x18 0x18 0x18 0x18 0x00 0x18 // ;34
        0x00 0x6c 0x6c 0x24 0x00 0x00 0x00 0x00 // ;35
        0x00 0x24 0x7e 0x24 0x7e 0x24 0x24 0x00 // ;36
        0x14 0x3e 0x54 0x54 0x3e 0x15 0x15 0x3e // ;37
        0x00 0x22 0x54 0x28 0x12 0x25 0x42 0x00 // ;38
        0x38 0x44 0x44 0x28 0x32 0x4a 0x7c 0x00 // ;39
        0x00 0x60 0x60 0x20 0x00 0x00 0x00 0x00 // ;40
        0x00 0x08 0x10 0x10 0x10 0x10 0x08 0x00 // ;41
        0x00 0x10 0x08 0x08 0x08 0x08 0x10 0x00 // ;42
        0x00 0x10 0x54 0x38 0x10 0x38 0x54 0x00 // ;43
        0x00 0x00 0x10 0x10 0x7c 0x10 0x10 0x00 // ;44
        0x00 0x00 0x00 0x00 0x60 0x60 0x20 0x00 // ;45
        0x00 0x00 0x00 0x7e 0x00 0x00 0x00 0x00 // ;46
        0x00 0x00 0x00 0x00 0x00 0x60 0x60 0x00 // ;47
        0x00 0x02 0x04 0x08 0x10 0x20 0x40 0x00 // ;48
        0x00 0x38 0x4c 0x54 0x54 0x64 0x38 0x00 // ;49
        0x00 0x10 0x30 0x10 0x10 0x10 0x38 0x00 // ;50
        0x00 0x38 0x44 0x04 0x38 0x40 0x7c 0x00 // ;51
        0x00 0x78 0x04 0x38 0x04 0x04 0x78 0x00 // ;52
        0x00 0x44 0x44 0x44 0x7c 0x04 0x04 0x00 // ;53
        0x00 0x7c 0x40 0x78 0x04 0x04 0x78 0x00 // ;54
        0x00 0x38 0x40 0x78 0x44 0x44 0x38 0x00 // ;55
        0x00 0x7c 0x44 0x08 0x08 0x10 0x10 0x00 // ;56
        0x00 0x38 0x44 0x38 0x44 0x44 0x38 0x00 // ;57
        0x00 0x38 0x44 0x44 0x3c 0x04 0x38 0x00 // ;58
        0x00 0x18 0x18 0x00 0x00 0x18 0x18 0x00 // ;59
        0x00 0x18 0x18 0x00 0x00 0x18 0x18 0x08 // ;60
        0x00 0x08 0x10 0x20 0x40 0x20 0x10 0x08 // ;61
        0x00 0x00 0x7c 0x00 0x7c 0x00 0x00 0x00 // ;62
        0x00 0x20 0x10 0x08 0x04 0x08 0x10 0x20 // ;63
        0x00 0x38 0x44 0x04 0x18 0x10 0x00 0x10 // ;64
        0x00 0x3c 0x5a 0x6a 0x5a 0x46 0x3c 0x00 // ;65
        0x00 0x10 0x28 0x28 0x7c 0x44 0x44 0x00 // ;66
        0x00 0x78 0x44 0x78 0x44 0x44 0x78 0x00 // ;67
        0x00 0x38 0x44 0x40 0x40 0x44 0x38 0x00 // ;68
        0x00 0x78 0x44 0x44 0x44 0x44 0x78 0x00 // ;69
        0x00 0x7c 0x40 0x7c 0x40 0x40 0x7c 0x00 // ;70
        0x00 0x7c 0x40 0x40 0x78 0x40 0x40 0x00 // ;71
        0x00 0x38 0x44 0x40 0x4e 0x44 0x38 0x00 // ;72
        0x00 0x44 0x44 0x7c 0x44 0x44 0x44 0x00 // ;73
        0x00 0x38 0x10 0x10 0x10 0x10 0x38 0x00 // ;74
        0x00 0x1c 0x08 0x08 0x08 0x48 0x30 0x00 // ;75
        0x00 0x44 0x48 0x50 0x68 0x44 0x44 0x00 // ;76
        0x00 0x40 0x40 0x40 0x40 0x40 0x7c 0x00 // ;77
        0x00 0x44 0x6c 0x54 0x44 0x44 0x44 0x00 // ;78
        0x00 0x44 0x64 0x54 0x54 0x4c 0x44 0x00 // ;79
        0x00 0x38 0x44 0x44 0x44 0x44 0x38 0x00 // ;80
        0x00 0x78 0x44 0x44 0x78 0x40 0x40 0x00 // ;81
        0x00 0x38 0x44 0x44 0x54 0x4c 0x3c 0x06 // ;82
        0x00 0x78 0x44 0x44 0x78 0x44 0x44 0x00 // ;83
        0x00 0x3c 0x40 0x38 0x04 0x44 0x38 0x00 // ;84
        0x00 0x7c 0x10 0x10 0x10 0x10 0x10 0x00 // ;85
        0x00 0x44 0x44 0x44 0x44 0x44 0x38 0x00 // ;86
        0x00 0x44 0x44 0x28 0x28 0x10 0x10 0x00 // ;87
        0x00 0x54 0x54 0x54 0x54 0x28 0x28 0x00 // ;88
        0x00 0x44 0x28 0x10 0x10 0x28 0x44 0x00 // ;89
        0x00 0x44 0x44 0x28 0x10 0x10 0x10 0x00 // ;90
        0x00 0x7c 0x04 0x08 0x10 0x20 0x7c 0x00 // ;91
        0x00 0x38 0x20 0x20 0x20 0x20 0x38 0x00 // ;92
        0x00 0x44 0x28 0x7c 0x10 0x7c 0x10 0x00 // ;93
        0x00 0x38 0x08 0x08 0x08 0x08 0x38 0x00 // ;94
        0x00 0x10 0x28 0x44 0x00 0x00 0x00 0x00 // ;95
        0x00 0x00 0x00 0x00 0x00 0x00 0x7c 0x00 // ;96
        0x00 0x30 0x30 0x10 0x00 0x00 0x00 0x00 // ;97
        0x00 0x00 0x38 0x04 0x3c 0x44 0x3c 0x00 // ;98
        0x00 0x40 0x40 0x78 0x44 0x44 0x78 0x00 // ;99
        0x00 0x00 0x38 0x44 0x40 0x44 0x38 0x00 // ;100
        0x00 0x04 0x04 0x3c 0x44 0x44 0x3c 0x00 // ;101
        0x00 0x00 0x38 0x44 0x7c 0x40 0x3c 0x00 // ;102
        0x00 0x08 0x10 0x38 0x10 0x10 0x10 0x00 // ;103
        0x00 0x00 0x3c 0x44 0x3c 0x44 0x38 0x00 // ;104
        0x00 0x40 0x40 0x78 0x44 0x44 0x44 0x00 // ;105
        0x00 0x10 0x00 0x30 0x10 0x10 0x38 0x00 // ;106
        0x00 0x10 0x00 0x10 0x10 0x10 0x60 0x00 // ;107
        0x00 0x20 0x24 0x28 0x30 0x28 0x24 0x00 // ;108
        0x00 0x10 0x10 0x10 0x10 0x10 0x10 0x00 // ;109
        0x00 0x00 0x78 0x54 0x54 0x54 0x54 0x00 // ;110
        0x00 0x00 0x78 0x44 0x44 0x44 0x44 0x00 // ;111
        0x00 0x00 0x38 0x44 0x44 0x44 0x38 0x00 // ;112
        0x00 0x00 0x78 0x44 0x78 0x40 0x40 0x00 // ;113
        0x00 0x04 0x3c 0x44 0x3c 0x04 0x04 0x00 // ;114
        0x00 0x00 0x5c 0x60 0x40 0x40 0x40 0x00 // ;115
        0x00 0x00 0x3c 0x40 0x38 0x04 0x78 0x00 // ;116
        0x00 0x20 0x78 0x20 0x20 0x20 0x18 0x00 // ;117
        0x00 0x00 0x48 0x48 0x48 0x48 0x34 0x00 // ;118
        0x00 0x00 0x44 0x44 0x44 0x28 0x10 0x00 // ;119
        0x00 0x00 0x54 0x54 0x54 0x54 0x28 0x00 // ;120
        0x00 0x00 0x44 0x28 0x10 0x28 0x44 0x00 // ;121
        0x00 0x00 0x44 0x28 0x10 0x10 0x20 0x00 // ;122
        0x00 0x00 0x7c 0x08 0x10 0x20 0x7c 0x00 // ;123
        0x00 0x08 0x10 0x10 0x20 0x10 0x10 0x08 // ;124
        0x00 0x10 0x10 0x10 0x10 0x10 0x10 0x00 // ;125
        0x00 0x10 0x08 0x08 0x04 0x08 0x08 0x10 // ;126
        0x00 0x32 0x4c 0x00 0x00 0x00 0x00 0x00 // ;127
        0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 // ;128
        0x00 0x18 0x3c 0x7e 0x7e 0x18 0x3c 0x00 // ;129
        0x00 0x24 0x7e 0x7e 0x7e 0x3c 0x18 0x00 // ;130
        0x00 0x18 0x18 0x7e 0x7e 0x18 0x3c 0x00 // ;131
        0x00 0x18 0x3c 0x7e 0x7e 0x3c 0x18 0x00 // ;132
        0x00 0x3c 0x42 0x42 0x42 0x42 0x3c 0x00 // ;133
        0x00 0x3c 0x7e 0x7e 0x7e 0x7e 0x3c 0x00 // ;134
        0x00 0x10 0x7c 0x20 0x78 0x54 0x28 0x3c // ;135
        0x00 0x00 0x10 0x7c 0x10 0x7c 0x5c 0x74 // ;136
        0x00 0x00 0x00 0x00 0x48 0x44 0x44 0x24 // ;137
        0x00 0x00 0x20 0x10 0x78 0x08 0x08 0x10 // ;138
        0x00 0x00 0x20 0x10 0x78 0x08 0x30 0x48 // ;139
        0x00 0x00 0x10 0x7c 0x14 0x78 0x54 0x34 // ;140
        0x00 0x00 0x00 0x50 0xf8 0x54 0x48 0x20 // ;141
        0x00 0x00 0x00 0x50 0x7c 0x54 0x18 0x20 // ;142
        0x00 0x00 0x00 0x10 0x1c 0x30 0x58 0x34 // ;143
        0x00 0x00 0x00 0x00 0x78 0x04 0x04 0x38 // ;144
        0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 // ;145
        0x00 0x20 0xf8 0x20 0x7c 0xac 0xb4 0x74 // ;146
        0x00 0x00 0x88 0x88 0x84 0x84 0xa4 0x40 // ;147
        0x00 0x60 0x00 0x70 0x88 0x08 0x10 0x60 // ;148
        0x00 0x60 0x10 0xf8 0x20 0x60 0x50 0x98 // ;149
        0x00 0x24 0xf4 0x20 0x78 0xa4 0xa4 0x68 // ;150
        0x00 0x48 0xe4 0x54 0x50 0x90 0x10 0x60 // ;151
        0x00 0x40 0xf8 0x20 0xf8 0x10 0x80 0x70 // ;152
        0x00 0x20 0x20 0x40 0x80 0x40 0x20 0x20 // ;153
        0x00 0x88 0x88 0xbc 0x88 0x88 0x88 0x50 // ;154
        0x00 0x00 0xf8 0x08 0x00 0x00 0x80 0x78 // ;155
        0x00 0x20 0xf8 0x20 0x20 0x00 0x80 0x70 // ;156
        0x00 0x80 0x80 0x80 0x80 0x80 0x88 0x70 // ;157
        0x00 0x10 0xfc 0x30 0x50 0x30 0x10 0x60 // ;158
        0x00 0x48 0xfc 0x48 0x48 0x48 0x40 0x38 // ;159
        0x00 0x44 0x28 0xfc 0x30 0x40 0x40 0x38 // ;160
        0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 // ;161
        0x00 0x00 0x00 0x00 0x08 0x14 0x08 0x00 // ;162
        0x00 0x60 0x40 0x40 0x00 0x00 0x00 0x00 // ;163
        0x00 0x00 0x00 0x00 0x04 0x04 0x0c 0x00 // ;164
        0x00 0x00 0x00 0x00 0x10 0x08 0x08 0x00 // ;165
        0x00 0x00 0x00 0x30 0x30 0x00 0x00 0x00 // ;166
        0x00 0x00 0x00 0x7c 0x04 0x7c 0x04 0x38 // ;167
        0x00 0x00 0x00 0x7c 0x04 0x14 0x10 0x20 // ;168
        0x00 0x00 0x00 0x08 0x18 0x70 0x10 0x10 // ;169
        0x00 0x00 0x00 0x10 0x7c 0x44 0x04 0x18 // ;170
        0x00 0x00 0x00 0x00 0x38 0x10 0x10 0x7c // ;171
        0x00 0x00 0x00 0x08 0x7c 0x18 0x28 0x48 // ;172
        0x00 0x00 0x00 0x20 0x7c 0x24 0x24 0x20 // ;173
        0x00 0x00 0x00 0x00 0x38 0x08 0x08 0x7c // ;174
        0x00 0x00 0x00 0x7c 0x04 0x7c 0x04 0x7c // ;175
        0x00 0x00 0x00 0x54 0x54 0x04 0x04 0x18 // ;176
        0x00 0x00 0x00 0x40 0x3c 0x00 0x00 0x00 // ;177
        0x00 0x7c 0x04 0x14 0x14 0x14 0x20 0x40 // ;178
        0x00 0x04 0x08 0x18 0x30 0x50 0x10 0x10 // ;179
        0x00 0x10 0x7c 0x44 0x44 0x04 0x08 0x10 // ;180
        0x00 0x00 0x7c 0x10 0x10 0x10 0x10 0x7c // ;181
        0x00 0x08 0x7c 0x08 0x18 0x28 0x48 0x18 // ;182
        0x00 0x10 0x7c 0x14 0x24 0x24 0x44 0x08 // ;183
        0x00 0x10 0x7c 0x10 0x7c 0x10 0x10 0x10 // ;184
        0x00 0x20 0x3c 0x24 0x44 0x08 0x08 0x30 // ;185
        0x00 0x20 0x3c 0x28 0x48 0x08 0x10 0x20 // ;186
        0x00 0x00 0x7c 0x04 0x04 0x04 0x04 0x7c // ;187
        0x00 0x28 0x7c 0x28 0x28 0x08 0x08 0x30 // ;188
        0x00 0x64 0x04 0x64 0x04 0x04 0x08 0x70 // ;189
        0x00 0x7c 0x04 0x04 0x08 0x10 0x28 0x44 // ;190
        0x00 0x20 0x7c 0x24 0x20 0x20 0x20 0x1c // ;191
        0x00 0x44 0x44 0x44 0x04 0x04 0x08 0x30 // ;192
        0x00 0x1c 0x24 0x24 0x7c 0x04 0x08 0x10 // ;193
        0x00 0x04 0x78 0x10 0x7c 0x10 0x10 0x20 // ;194
        0x00 0x54 0x54 0x54 0x04 0x04 0x08 0x30 // ;195
        0x00 0x38 0x00 0x7c 0x10 0x10 0x10 0x20 // ;196
        0x00 0x20 0x20 0x20 0x38 0x24 0x20 0x20 // ;197
        0x00 0x10 0x7c 0x10 0x10 0x10 0x10 0x20 // ;198
        0x00 0x00 0x38 0x00 0x00 0x00 0x00 0x7c // ;199
        0x00 0x7c 0x04 0x48 0x30 0x10 0x28 0x44 // ;200
        0x00 0x10 0x7c 0x08 0x38 0x54 0x54 0x10 // ;201
        0x00 0x08 0x08 0x08 0x08 0x10 0x10 0x60 // ;202
        0x00 0x08 0x08 0x48 0x44 0x44 0x44 0x44 // ;203
        0x00 0x40 0x78 0x40 0x40 0x40 0x40 0x3c // ;204
        0x00 0x00 0x7c 0x04 0x04 0x08 0x08 0x30 // ;205
        0x00 0x00 0x20 0x50 0x48 0x04 0x04 0x00 // ;206
        0x00 0x10 0x7c 0x10 0x10 0x54 0x54 0x10 // ;207
        0x00 0x00 0x7c 0x04 0x48 0x30 0x10 0x08 // ;208
        0x00 0x60 0x1c 0x60 0x1c 0x00 0x60 0x1c // ;209
        0x00 0x10 0x20 0x28 0x48 0x44 0x7c 0x04 // ;210
        0x00 0x04 0x04 0x28 0x10 0x18 0x24 0x40 // ;211
        0x00 0x7c 0x20 0x20 0x7c 0x20 0x20 0x1c // ;212
        0x00 0x20 0xfc 0x24 0x24 0x10 0x10 0x10 // ;213
        0x00 0x78 0x08 0x08 0x08 0x08 0x08 0x7c // ;214
        0x00 0x7c 0x04 0x04 0x7c 0x04 0x04 0x7c // ;215
        0x00 0x38 0x00 0x7c 0x04 0x04 0x04 0x38 // ;216
        0x00 0x48 0x48 0x48 0x48 0x08 0x08 0x30 // ;217
        0x00 0x48 0x48 0x48 0x48 0x48 0x48 0x8c // ;218
        0x00 0x40 0x40 0x40 0x40 0x44 0x48 0x70 // ;219
        0x00 0x7c 0x44 0x44 0x44 0x44 0x44 0x7c // ;220
        0x00 0x7c 0x44 0x44 0x04 0x04 0x04 0x38 // ;221
        0x00 0x64 0x04 0x04 0x04 0x08 0x08 0x70 // ;222
        0x00 0x00 0x50 0x28 0x00 0x00 0x00 0x00 // ;223
        0x00 0x20 0x50 0x20 0x00 0x00 0x00 0x00 // ;224
        0x00 0x20 0x78 0x20 0x5c 0x40 0x40 0x9c // ;225
        0x00 0x20 0x7c 0x20 0x78 0x84 0x04 0x18 // ;226
        0x00 0x00 0x38 0xc4 0x04 0x04 0x04 0x78 // ;227
        0x00 0x00 0x3c 0xc8 0x10 0x20 0x20 0x18 // ;228
        0x00 0x84 0x48 0x30 0x20 0x40 0x40 0x3c // ;229
        0x00 0x88 0x64 0x60 0x90 0x38 0x50 0x30 // ;230
        0x00 0x40 0x5c 0x80 0x80 0x80 0x80 0x5c // ;231
        0x00 0x80 0x44 0x78 0xd8 0xa8 0xbc 0x58 // ;232
        0x00 0x48 0xd8 0x68 0x48 0xd8 0xec 0x58 // ;233
        0x00 0x00 0x38 0x54 0x94 0xa4 0xa4 0x68 // ;234
        0x00 0x08 0xbc 0x88 0x88 0xb8 0xac 0xb4 // ;235
        0x00 0x00 0x6c 0xa8 0x48 0x48 0x48 0x30 // ;236
        0x00 0x00 0x70 0x00 0x20 0x10 0x94 0xa4 // ;237
        0x00 0x00 0x00 0x30 0x48 0x88 0x84 0x04 // ;238
        0x00 0x00 0x9c 0x88 0xbc 0x98 0xac 0x58 // ;239
        0x00 0x20 0xfc 0x20 0xfc 0x70 0xa8 0x64 // ;240
        0x00 0x00 0xe0 0x28 0x78 0xac 0xa8 0x50 // ;241
        0x00 0x28 0xf4 0x20 0x60 0xa4 0x64 0x38 // ;242
        0x00 0x84 0x74 0x48 0xb4 0x94 0xac 0x48 // ;243
        0x00 0x20 0xf8 0x40 0xfc 0x40 0x40 0x38 // ;244
        0x00 0x90 0x5c 0x74 0xd4 0x48 0x40 0x20 // ;245
        0x00 0x90 0xb8 0xd4 0x94 0x1c 0x10 0x20 // ;246
        0x00 0x20 0x38 0x20 0x70 0xa8 0xa8 0x60 // ;247
        0x00 0x40 0x20 0x80 0xb8 0xc8 0x08 0x30 // ;248
        0x00 0x90 0x88 0x88 0x88 0x08 0x10 0x20 // ;249
        0x00 0x78 0x10 0x30 0xc8 0x38 0x48 0x30 // ;250
        0x00 0x40 0xe8 0x58 0x68 0x48 0xc8 0x4c // ;251
        0x00 0x78 0x08 0x30 0x48 0x84 0x04 0x38 // ;252
        0x00 0x40 0xe0 0x58 0x64 0x44 0xc4 0x58 // ;253
        0x00 0x10 0x10 0x20 0x30 0x50 0x68 0xcc // ;254
        0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 // ;255
        0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 // ;256
    ]

    // -- end: font_jp.asm

    // -- end: font.asm

    // ;
    // ; This routine is called just after displaying the logo.
    // ; This fixes the Mirai sprite garbage bug.
    // ;

    // -- begin: slot.asm

    // ;-------------------------------------
    // ; 000Ch RDSLT
    // ; Reads a value from an address in another slot.
    // ; Input:   A  = slot ID: E000SSPP
    // ;          HL = address to read
    // ; Output:  A  = value read
    // ;          Interrupts disabled.
    // ; Changes: F, C, DE
    proc rdslt() {
        BC -push
        HL -push
        AF -push
        D <- A                           // ; init D in case call is not made
        A & A                            // ; expanded slot?
        DI
        CALL M? select_subslot

        AF -pop
        HL -pop -push                    // ; HL = address
        DE -push                         // ; D = slot ID, E = saved SSL
        HL -push                         // ; HL = address
        AF -push                         // ; A = slot ID
        @H <* 2 & 0x03 -> L              // ; L = page number
           -> B
        A <- 0xfc
        rdsft()

        E <- A                           // ; E = mask (shifted)
        B <- L                           // ; B = page number
        AF -pop                          // ; A = slot ID
        A & 0x03
        rdsft()

        B <- A                           // ; B = primary slot (shifted)
        A -in PSL_STAT -> D              // ; D = primary slot select for restore
          & E | B                        // ; A = primary slot select for read
        HL -pop                          // ; HL = address
        CALL rdprim

        A <- E
        DE -pop                          // ; D = slot ID, E = saved SSL
        AF -push                         // ; A = value read
        D -bit? 7                        // ; expanded slot?
        CALL NZ? restore_subslot

        AF -pop                          // ; A = value read
        HL -pop                          // ; HL = address
        BC -pop
        return
    }

    proc rdsft() {
        B ++ --
        return-if Z?

    rdsft_lp: /*local*/
        A <* 2
        DJNZ rdsft_lp
        return
    }

    // ;-------------------------------------
    // ; $0014 WRSLT
    // ; Writes a value to an address in another slot.
    // ; Input:   A  = slot ID: E000SSPP
    // ;          HL = address to write
    // ;          E  = value to write
    // ; Output:  Interrupts disabled.
    // ; Changes: AF, BC, D
    proc wrslt() {
        HL -push
        D <- A                           // ; D = slot ID
        DE -push
        A & A                            // ; expanded slot?
        DI
        CALL M? select_subslot

        BC -pop                          // ; B = slot ID, C = data
        HL -pop
        DE -push                         // ; D = slot ID, E = saved SSL
        HL -push                         // ; HL = address
        @H <* 2 & 0x03 -> L              // ; L = page number
           -> B                          // ; B = page number
        A <- 0xfc
        rdsft()

        E <- A                           // ; E = mask (shifted)
        B <- L                           // ; B = page number
        @D & 0x03                        // ; A = 000000PP
        rdsft()

        B <- A                           // ; B = primary slot (shifted)
        A -in PSL_STAT -> D              // ; D = primary slot select for restore
          & E | B                        // ; A = primary slot select for write
        HL -pop                          // ; HL = address
        E <- C                           // ; E = data
        CALL wrprim

        DE -pop                          // ; D = slot ID, E = saved SSL
        HL -push                         // ; HL = address
        D -bit? 7                        // ; expanded slot?
        CALL NZ? restore_subslot

        HL -pop
        return
    }

    // ;-------------------------------------
    // ; $001C CALSLT
    // ; Function : Executes inter-slot call.
    // ; Input    : IY - High byte with input for A in RDSLT
    // ;            IX - The address that will be called
    // ; Remark   : Variables can never be given in alternative registers
    // ;            of the Z-80 or IX and IY

    proc calslt() {
        AF <-> AF-
        EXX

        // ; Select secondary slot of target:
        // ; Note: This approach fails if target is in page 0 of slot 0.1, 0.2 or 0.3.
        // ; TODO: Put slot 0 specific routine in page 3, on the stack if necessary.
        DI

        IY -push
        AF -pop                          // ; A = slot ID: E000SSPP
        IX -push
        HL -pop                          // ; HL = address to call
        D <- A                           // ; init D in case call is not made
        A & A                            // ; expanded slot?
        CALL M? select_subslot

        DE -push                         // ; D = slot ID, E = saved SSL

        // ; Calculate primary slot select value:
        A <- D                           // ; A = slot ID: E000SSPP
          & 0x03 -> B                    // ; B = primary slot
        C <- 0xfc                        // ; C = mask

        // ; Calculate page that contains call address.
        IX -push
        AF -pop                          // ; A = high byte call address
        A <* 2 & 0x03                    // ; A = page

        // ; Shift B and C page*2 positions to the left.
        A + A
        JR Z? calslt_sh2

    calslt_sh1: /*local*/
        B <* 1
        C <* 1
        A --
        JR NZ? calslt_sh1

    calslt_sh2: /*local*/
        // ; Select primary slot of target and perform call:
        HL@calslt_restore -push
        A -in PSL_STAT
        AF -push
        A & C                            // ; C = mask (shifted)
          | B                            // ; B = primary slot (shifted)
        EXX
        JP clprim

    calslt_restore: /*local*/
        AF <-> AF-
        EXX

        // ; Restore secondary slot:
        DI

        DE -pop                          // ; D = slot ID, E = saved SSL
        D -bit? 7                        // ; expanded slot?
        CALL NZ? restore_subslot

        // ; Done:
        AF <-> AF-
        EXX
        return
    }

    // ;-------------------------------------
    // ; $0024 ENASLT
    // ; Selects a slot in the page specified by an address.
    // ; Input:   A  = slot ID: ExxxSSPP
    // ;               E  = expanded flag
    // ;               SS = secondary slot number (only if expanded)
    // ;               PP = primary slot number
    // ;          HL = address inside the page to change
    // ; Output:  Interrupts disabled.
    // ; Changes: AF, BC, DE

    proc enaslt() {
        // ; A=(A >> 6)&0x3
        DI

        HL -push
        L <- A                           // ; L = ExxxSSPP
        A & 0x03                         // ; A = 000000PP
          -> B
        A <- 0xab

    psl_dup_lp: /*local*/
        A + 0x55
        B --
        JP P? psl_dup_lp

        D <- A                           // ; D = PP PP PP PP
        @H <* 2 & 0x03 -> H              // ; H = page number (0-3)
           -> B
        A <- 0xc0

    page_msk_lp: /*local*/
        A <* 2
        B --
        JP P? page_msk_lp

        E <- A                           // ; E = page mask (00 00 00 11 << page)
        A -not -> C                      // ; C = page mask complement
        @D & E -> B                      // ; B = 00 00 00 PP << page
        @L & A
        JP P? chg_psl

        // ;SSL-Change
        A >* 2 & 0x03                    // ; A = 000000SS
        HL -push
        BC -push
        B <- A
        A <- 0xab

    ssl_dup_lp: /*local*/
        A + 0x55
        B --
        JP P? ssl_dup_lp

        A & E -> B                       // ; B = 00 00 00 SS << page
        @D & 0xc0 -> H
        A -in PSL_STAT -> L & 0xc0 | H
          -out PSL_STAT
        @[SSL_REGS] -not & C             // ; preserve other pages
            | B -> C -> [SSL_REGS]
        @L -out PSL_STAT

        // ; (SLTTBL + PP) <- RegC

        HL <- SLTTBL
        @D & 0x03                        // ; A = 000000PP
           + L -> L                      // ; L = L + A
        @H +$ 0 -> H                     // ; H = H + Cy
        @C -> [HL]
        BC -pop
        HL -pop

    chg_psl: /*local*/
        A -in PSL_STAT & C | B
          -out PSL_STAT
        HL -pop
        return
    }

    // ;--------------------------------
    // ; Select subslot.
    // ; Input:   A  = slot ID: E000SSPP
    // ;          HL = address which specifies page to select
    // ;               (actually, only the highest 2 bits of H are relevant)
    // ; Output:  D  = slot ID (same as input)
    // ;          E  = original value of secondary slot select register
    // ;          SLTTBL[slot] = new value of secondary slot select register
    // ; Changes: AF, HL, BC
    // ; Note:    Interrupts must be disabled before calling this routine.
    proc select_subslot() {
        // ; Select primary slot of target in page 3.
        // ; Note: Stack is unavailable until primary slot is restored.
        D <- A                           // ; D = E000SSPP
        A >* 2 -> E                      // ; E = PPE000SS
          & 0xc0 -> L                    // ; L = PP000000
        A -in PSL_STAT -> C              // ; C = saved PSL
          & 0x3f | L -out PSL_STAT

        // ; Shift mask and subslot according to page.
        A <- E                           // ; A = PPE000SS
          & 0x03 -> L                    // ; L = subslot
        A <- H                           // ; A = high byte of address
        H <- 0x03                        // ; H = mask
        JR select_subslot_next

    select_subslot_lp: /*local*/
        HL + HL                          // ; Shift 2 bits to the left.
           + HL

    select_subslot_next: /*local*/
        A - 0x40                         // ; Subtract 1 page.
        JR NC? select_subslot_lp

        @H -not -> H

        // ; Select secondary slot of target.
        @[SSL_REGS] -not -> E            // ; E = saved SSL
            & H                          // ; H = mask (shifted)
            | L                          // ; L = subslot (shifted)
            -> [SSL_REGS] -> L           // ; L = value written to SSL_REGS

        // ; Restore original primary slot in page 3.
        @C -out PSL_STAT

        // ; Update SLTTBL.
        @D & 0x03                        // ; A = 000000SS
           -> C
        B <- 0
        A <- L                           // ; A = value written to SSL_REGS
        HL@SLTTBL + BC
        A -> [HL]
        return
    }

    // ;--------------------------------
    // ; Restore subslot, companion routine to select_subslot.
    // ; Input:   D  = slot ID: E000SSPP
    // ;          E  = original value of secondary slot select register
    // ; Output:  SLTTBL[slot] = original value of secondary slot select register
    // ; Changes: AF, HL, BC
    // ; Note:    Interrupts must be disabled before calling this routine.
    proc restore_subslot() {
        // ; Select primary slot of target in page 3.
        // ; Note: Stack is unavailable until primary slot is restored.
        @D >* 2 & 0xc0 -> B              // ; B = PP000000
        A -in PSL_STAT -> C              // ; C = saved PSL
          & 0x3f | B -out PSL_STAT

        // ; Restore secondary slot.
        @E -> [SSL_REGS]

        // ; Restore original primary slot in page 3.
        @C -out PSL_STAT

        // ; Update SLTTBL.
        @D & 0x03                        // ; A = 000000SS
           -> C
        B <- 0
        HL@SLTTBL + BC
        E -> [HL]
        return
    }

    // ;--------------------------------

    proc m_rdprim() {
        A -out PSL_STAT
        E <- [HL]
        JR m_wrprm1
    }

    proc m_wrprim() {
        A -out PSL_STAT
        E -> [HL]
        fallthrough
    }

    proc m_wrprm1() {
        @D -out PSL_STAT
        return
    }

    proc m_clprim() {
        A -out PSL_STAT
        AF <-> AF-
        CALL cl_jp

        AF <-> AF- -pop
        A -out PSL_STAT
        AF <-> AF-
        return
    }

    proc m_cl_jp() {
        JP [IX]
    }

    m_prim_end:
    NOP
    const rdprim = 0xf380
    const wrprim = rdprim + (m_wrprim - m_rdprim)
    const clprim = rdprim + (m_clprim - m_rdprim)
    const cl_jp = rdprim + (m_cl_jp - m_rdprim)

    // ; vim:ts=8:expandtab:filetype=z8a:syntax=z8a:
    // -- end: slot.asm

    // ;---------------------------------
    // ; system messages
    // ;---------------------------------

    str_proginfo:
    // ;       [01234567890123456789012345678]
    // ;db     "C-BIOS 1.23      cbios.sf.net"
    // -- begin: ../derived/asm/version.asm
    data byte ["C-BIOS 0.29      cbios.sf.net"]

    // -- end: ../derived/asm/version.asm
    data byte [0x0d 0x0a]

    // -- begin: locale.asm
    data byte [
        "Character set: "
        "JP"
        0x0d 0x0a
        "Interrupt frequency: "
        "6"
        "0Hz"
    ]

    // ; the text fits exactly a line for non-MSX1 VDP
    data byte [
        0x0d 0x0a
        "Keyboard type: "
        "JP"
        0x0d 0x0a

        // ;BASIC type is not very interesting without BASIC
        // ;db      "BASIC type: "
        // ;IF LOCALE_BASIC = LOCAL_BASIC_US
        // ;        db      "US"
        // ;ENDIF
        // ;IF LOCALE_BASIC = LOCAL_BASIC_JP
        // ;        db      "JP"
        // ;ENDIF
        0x0d 0x0a
    ]

    // -- end: locale.asm
    data byte [0x0d 0x0a 0x0d 0x0a 0x00]
    const str_proginfo_length = __PC__ - str_proginfo

    data str_slot = byte [
        // ;       [01234567890123456789012345678]
        "Init ROM in slot: " 0x00
    ]

    data str_basic = byte [
        // ;       [01234567890123456789012345678]
        "Cannot execute a BASIC ROM." 0x0d 0x0a 0x00
    ]

    // ;-------------------------------------
    // ; error messages
    data str_error_prompt = byte [
        "ERROR:" 0x00
    ]

    data str_memory_err = byte [
        "MEMORY NOT FOUND." 0x00
    ]

    data str_no_basic_intr = byte [
        "CALLED NON EXISTING BASIC." 0x00
    ]

    data str_stack_error = byte [
        "STACK ERROR." 0x00
    ]

    data str_nocart = byte [
        // ;       [01234567890123456789012345678]
        0x0d 0x0a 0x0d 0x0a
        "No cartridge found." 0x0d 0x0a
        0x0d 0x0a
        "This version of C-BIOS can" 0x0d 0x0a
        "only start cartridges." 0x0d 0x0a
        "Please restart your MSX" 0x0d 0x0a
        "(emulator) with a cartridge" 0x0d 0x0a
        "inserted." 0x00
    ]

    // ;-------------------------------------
    // ; scan code tables

    // -- begin: scancodes_jp.asm

    // ; Scan code tables JIS (japanese) keybaord for C-BIOS
    // ;
    // ; Copyright (c) 2008 Eric Boon.  All rights reserved.
    // ;
    // ; 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.
    // ;
    // ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
    // ;
    // ; -------------------------------------
    // ; scan code tableS
    data scode_tbl = byte [
        // ; Japanese
        "01234567"                       // ;00
        "89-^" 0x5c "@[;"                // ;01 ($5C = yen)
        ":],./_ab"                       // ;02
        "cdefghij"                       // ;03
        "klmnopqr"                       // ;04
        "stuvwxyz"                       // ;05
    ]

    data scode_tbl_shift = byte [
        "0!" 0x22 "#$%&'"                // ;00 ($22 = quote)
        "()=~|`{+"                       // ;01
        "*}<>?_AB"                       // ;02
        "CDEFGHIJ"                       // ;03
        "KLMNOPQR"                       // ;04
        "STUVWXYZ"                       // ;05
    ]

    data scode_tbl_graph = byte [ /*unused*/
        0x0f 0x07 0x01 0x02 0x03 0x04 0x05 0x06 // ;00
        0x0d 0xe0 0x17 0x00 0x09 0x00 0x84 0x82 // ;01
        0x81 0x85 0x1f 0x1d 0x80 0x83 0x00 0x1b // ;02
        0x1a 0x14 0x18 0x15 0x13 0x0a 0x16 0x00 // ;03
        0x00 0x1e 0x0b 0x00 0x00 0x10 0x00 0x12 // ;04
        0x0c 0x19 0x00 0x11 0x00 0x1c 0x08 0x00 // ;05
    ]

    // ; TODO SHIFT + GRAPH is undefined in the red book. Same keycodes
    // ;      as with shift or no key entered? (all $00)
    data scode_tbl_shift_graph = byte [ /*unused*/
        0x0f 0x07 0x01 0x02 0x03 0x04 0x05 0x06 // ;00
        0x0d 0xe0 0x17 0x00 0x09 0x00 0x84 0x82 // ;01
        0x81 0x85 0x1f 0x1d 0x80 0x83 0x00 0x1b // ;02
        0x1a 0x14 0x18 0x15 0x13 0x0a 0x16 0x00 // ;03
        0x00 0x1e 0x0b 0x00 0x00 0x10 0x00 0x12 // ;04
        0x0c 0x19 0x00 0x11 0x00 0x1c 0x08 0x00 // ;05
    ]

    data scode_tbl_kana = byte [ /*unused*/
        0xfc 0xe7 0xec 0x91 0x93 0x94 0x95 0xf4 // ;00
        0xf5 0xf6 0xee 0xed 0xb0 0xde 0xdf 0xfa // ;01
        0x99 0xf1 0xe8 0xf9 0xf2 0xfb 0xe1 0x9a // ;02
        0x9f 0x9c 0x92 0xea 0x97 0x98 0xe6 0xef // ;03
        0xe9 0xf8 0xf3 0xf0 0xf7 0x9e 0xe0 0x9d // ;04
        0xe4 0x96 0xe5 0xeb 0xe3 0x9b 0xfd 0xe2 // ;05
    ]

    data scode_tbl_caps_kana = byte [ /*unused*/
        0xdc 0xc7 0xcc 0xb1 0xb3 0xb4 0xb5 0xd4 // ;00
        0xd5 0xd6 0xce 0xcd 0xb0 0xde 0xdf 0xda // ;01
        0xb9 0xd1 0xc8 0xd9 0xd2 0xdb 0xc1 0xba // ;02
        0xbf 0xbc 0xb2 0xca 0xb7 0xb8 0xc6 0xcf // ;03
        0xc9 0xd8 0xd3 0xd0 0xd7 0xbe 0xc0 0xbd // ;04
        0xc4 0xb6 0xc5 0xcb 0xc3 0xbb 0xdd 0xc2 // ;05
    ]

    data scode_tbl_shift_kana = byte [ /*unused*/
        0x86 0x00 0x00 0x87 0x89 0x8a 0x8b 0x8c // ;00
        0x8d 0x8e 0x00 0x00 0x00 0x00 0xa2 0x00 // ;01
        0x00 0x83 0x84 0x81 0x85 0x00 0x00 0x00 // ;02
        0x00 0x00 0x88 0x00 0x00 0x00 0x00 0x00 // ;03
        0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 // ;04
        0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x8f // ;05
    ]

    data scode_tbl_shift_caps_kana = byte [ /*unused*/
        0xa6 0x00 0x00 0xa7 0xa9 0xaa 0xab 0xac // ;00
        0xad 0xae 0x00 0x00 0x00 0x00 0xa2 0x00 // ;01
        0x00 0xa3 0xa4 0xa1 0xa5 0x00 0x00 0x00 // ;02
        0x00 0x00 0xa8 0x00 0x00 0x00 0x00 0x00 // ;03
        0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 // ;04
        0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xaf // ;05
    ]

    // ; vim:ts=8:expandtab:filetype=z8a:syntax=z8a:
    // -- end: scancodes_jp.asm

    // ; the last rows are not locale specific
    data scode_tbl_otherkeys = byte [
        0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 // ;06
        0x00 0x00 0x1b 0x09 0x00 0x08 0x00 0x0d // ;07
        0x20 0x0c 0x00 0x00 0x1d 0x1e 0x1f 0x1c // ;08
        "*+/01234"                       // ;09
        "56789-,."                       // ;0a
    ]

    // ;-------------------------------------
    data vdp_bios = byte [
        0x00 0x80 0x70 0x81 0x00 0x82 0x01 0x84
        0xf5 0x87 0x00 0x40
    ]

    // -- begin: statements.asm

    // ; C-BASIC statements
    // ;
    // ; Copyright (c) 2005 BouKiCHi.  All rights reserved.
    // ;
    // ;
    // ; 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.
    // ;
    // ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
    // ;
    // ;

    // ; part of ROM Basic
    org> 0x3193

    // ;-----------------------------
    // ;$3193 - Multiply
    // ; In  : HL,DE
    // ; Out : HL and DAC
    // ; Regs : unknown
    // ; Note : this routine is not correctly yet
    proc multiple() { /*unused*/
        BC -push
        HL -push
        BC -pop

    multiple_lp: /*local*/
        DE --
        @E | D
        JR Z? multiple_fin

        HL + BC
        JR multiple_lp

    multiple_fin: /*local*/
        BC -pop
        return
    }

    proc rombas() { /*unused*/
        HL -push
        AF -push
        HL <- rombas_text
        print_debug()

        @0x20 -out 0x2e
        @D -out 0x2f
        @E -out 0x2f
        AF -pop
        HL -pop
        return

        data rombas_text = byte [ /*local*/
            "ROMBAS" 0
        ]
        org> 0x392e
        data word [
            // ; runloop table

            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy
        ]

        // ; statement table
        org> 0x39de
        data word [
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
            rombas_niy rombas_niy rombas_niy
        ]

    rombas_niy: /*local*/
        HL -push
        AF -push
        HL <- rombas_niy_text
        print_debug()

        AF -pop
        HL -pop
        return

        data rombas_niy_text = byte [ /*local*/
            "BASIC statements are not implemented yet" 0
        ]
    }

    // -- end: statements.asm

    // ; FM Music Macro is calling the routine(seems to display message).
    // ; in : HL(an address of string with null termination)
    org> 0x6678
    CALL prn_text                        // ; as a substitution

    // ; ????
    org> 0x77cd
    RET

    // ; Note: Below are a bunch of routines which do not adhere to any API that
    // ;       I know of. They are called directly by the NMS8250 disk ROM.
    // ;       For comparing the behaviour of the C-BIOS disk ROM which is under
    // ;       development with the known working NMS8250 disk ROM it is useful
    // ;       to be able to run either disk ROM in a C-BIOS machine. Therefore we
    // ;       have stubs for the routines that the NMS8250 disk ROM may call.

    // ; NMS8250 disk ROM can call to this address.
    org> 0x7d17
    PUSH HL
    PUSH AF
    LD HL unk7D17_text
    CALL print_debug
    POP AF
    POP HL
    RET

    data unk7D17_text = byte [
        "unknown@7D17" 0
    ]

    // ; NMS8250 disk ROM retrieves a pointer from this address.
    org> 0x7d2f
    data word [call_sdfscr]

    // ; NMS8250 disk ROM can call to this address.
    org> 0x7d31

    // ; Restore screen parameters from RTC and print welcome message.
    LD IX 0x0189                         // ; SETSCR
    CALL extrom

    // ; Print BASIC copyright message.
    RET

    // ; NMS8250 disk ROM calls to this address.
    // ; Restore screen parameters from RTC.
    proc call_sdfscr() {
        IX <- 0x0185                     // ; SDFSCR
        JP extrom

        // ; NMS8250 disk ROM can call to this address.
        org> 0x7e14
        HL -push
        AF -push
        HL <- unk7E14_text
        print_debug()

        AF -pop
        HL -pop
        return

        data unk7E14_text = byte [ /*local*/
            "unknown@7E14" 0
        ]

        // ; FM Music Macro is calling this unknown routine.
        org> 0x7e6b
        return
        org> 0x8000
    }

    // ; vim:ts=8:expandtab:filetype=z8a:syntax=z8a:
    // -- end: main.asm

    // ; vim:ts=8:expandtab:filetype=z8a:syntax=z8a:
    // -- end: main_msx1_jp.asm
}