eris2010

Documentation: http://frombelow.net/projects/eris2010/
Clone: git clone https://git.frombelow.net/eris2010.git
Log | Files | Refs | Submodules | README | LICENSE

commit 82c47766dfd4fd7d1a2490220735a7e38ee58777
parent a303e6a386627a93262f5c3cfb94a24a3ea48a6f
Author: Gerd Beuster <gerd@frombelow.net>
Date:   Fri, 18 Dec 2020 18:44:49 +0100

Support for ANSI terminal

Diffstat:
Mdoc/Documentation.txt | 143++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Ddoc/notizen.txt | 67-------------------------------------------------------------------
Dmisc/sd_card/card_info.csv | 312-------------------------------------------------------------------------------
Dmisc/sd_card/esp32_sdcard.py | 14--------------
Amisc/write_default_files.sh | 11+++++++++++
Aroms/boot/ansi.asm | 281+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/boot/ansi.inc | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mroms/boot/boot.asm | 834++-----------------------------------------------------------------------------
Aroms/boot/boot.inc | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Droms/boot/boot_macros.inc | 339-------------------------------------------------------------------------------
Aroms/boot/io.asm | 424+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/boot/io.inc | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/boot/lfsr.asm | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/boot/sd_card.asm | 266+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/boot/sd_card.inc | 48++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/boot/spi.asm | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/boot/stack.asm | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/boot/stack.inc | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msw/10print/10print.asm | 5+++--
Msw/SW.txt | 26+++++++++++++++++++++++---
Asw/ansi_test/Makefile | 3+++
Asw/ansi_test/ansi_test.asm | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asw/load_from_card/Makefile | 3+++
Asw/load_from_card/load_from_card.asm | 194+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsw/sd_card/Makefile | 3---
Dsw/sd_card/sd_card.asm | 182-------------------------------------------------------------------------------
Msw/serial_line_echo/serial_line_echo.asm | 4++--
Msw/stack_test/stack_test.asm | 37+++++++++++++++++++++----------------
Msw/ttt/ttt.asm | 6++++--
Msw/via_test/via_test.asm | 4++--
Mtools/boot.py | 1-
Atools/reset.py | 17+++++++++++++++++
32 files changed, 2157 insertions(+), 1798 deletions(-)

diff --git a/doc/Documentation.txt b/doc/Documentation.txt @@ -11,7 +11,7 @@ configuration (with a little help (an inverter of the AFT16V8B). See hw/bom.ods for details of the components. (This list also contains components not used in the current design.) -** Program upload and reset logic +** Reset logic Resets can be triggered in two ways: A reset button is connected to an NE555 in a monostable configuration. Alternatively, a reset can be @@ -20,25 +20,68 @@ is used for program upload into RAM. See section Boot Sequence. This implies that the computer will remain in reset state until a serial terminal is opened if DTR is connected. In order to avoid this, -e.g. because you want to run a program while not serial interface is +e.g. because you want to run a program while no serial interface is attached, you can either configure your serial interface to keep DTR low at all times (stty -F <serial interface> -hup) or you do not connect the DTR line of the USB2Serial converter. - Note that the reset line of the 65C02 is active on low. We use the AFT16V8B - which mainly provides the bus logic - as an inverter. ** Memory Map -Lowest 32K are RAM, highest 8K are ROM. ACAI is in between. See -hw/bus_logic/BUS_LOGIC.PLD for details. +Lowest 32K are RAM, highest 8K are ROM. ACAI and VIA are in +between. See hw/bus_logic/BUS_LOGIC.PLD for details. + +** Booting + +The "standard" ROM is roms/boot/boot.bin. This ROM includes the +standard library (for serial communication, accessing SD card, RNG, +...). It provides two methods to load a program: + +- Via serial line + +After a reset, the ROM listens for a serial data transmission at 19200 +BPS 8N1. The first byte is the number of half-blocks of 256 bit to be +loaded. (Block size is 512 bit, because this is the block size of SD +cards.) The number of half-blocks is followed by the data. Data is +stored at $0200. Once the upload is completed. The upload program +returns a two-byte checksum. The first byte is the sum of all bytes +transmitted mod 256. The second byte is the xor of all bytes +transmitted. The upload program than starts executing the loaded +program at $0200. + +On the PC, use tools/boot.py for upload. + +- From SD Card + +The boot ROM includes sw/load_from_card/load_from_card.bin, the +program to load programs from SD card. This program is executed when +no program is transmitted via serial line. + +SD cards can store up to 10 programs. The filesystem format is as +follows: + +Block $00000000 is loaded. The first byte is the version of the +filesystem. It should by $00. + +The storage space for the first program starts at $00000001. The +storage space for the second program starts at $00010001, for the +third program at $00020001, ... Thus, each program has a total of +$ffff blocks of 512 bytes each available. The first block of each +program is the program header. It contains the number of blocks to be +loaded, and the app name, a null-terminated string. The actual program +code starts with the next block. The program is loaded to address +$0200 and executed. + +On the PC, use tools/gfs.py to write the SD card. ** Directory Structure - doc/ - Documentation - hw/ - Hardware description (pcb not complete yet) -- misc/ - Miscellaneous files - See section Tools for a description +- tools/ - PC programs to upload programs and write SD card +- misc/ - Miscellaneous files - See section Misc for a description - roms/ - ROM images. See roms/ROMS.txt for a description. - sw/ - "Userland" software to be loaded into RAM by a suitable ROM. See sw/SW.txt for a description. @@ -46,26 +89,62 @@ hw/bus_logic/BUS_LOGIC.PLD for details. ** Standard Library A standard library is part of the boot ROM located at roms/boot/. In -order to use it, import roms/boot/liba.h into your program. +order to use it, import roms/boot/boot.l and roms/boot/boot_macros.inc +into your program. The standard library includes a data stack with subroutines and macros for local variables and parameter passing. See roms/boot/boot.asm for documentation and sw/stack_test/stack_test.asm for examples. -** Boot Sequence +The following functionality is provided: + +*** Serial communication interface + +All serial communication is conduced at 19200BPS with 8N1. Various +input and output functions are provided, including a function that +initializes the RNG (see LFSR) by counting ticks until user input. + +*** LFSR + +A 16 bit LFSR with maximum period length is provided as PRNG. + +*** SPI -The ROM at roms/boot/boot.py loads data via the serial interface to -RAM starting at address $0300 and executes it. The first byte of the -transmission is the number of $100 byte blocks to be transmitted. Run -rom/boot/boot.py on the host PC to upload a program. This python -script triggers a reset by setting DTR to high; see section Reset Logic. +SPI message can send via the VIA. -If no initial byte is recevied, the boot ROM starts to execute a -standard application from ROM. +*** SD Card + +SD cards can accessed in SPI mode. All addresses are 32 bit, referring +to 512 bit blocks on the card. + +*** Data Stack + +A data stack in software for local variables and parameter passing. In +difference to most stacks, the stack grows from bottom to top. The +recommended memory layout is program code, followed by heap (if +applicable), followed by stack. You have to manually create and delete +stack frames with calls to create_stack_frame and delete_stack_frame. +Pull and push operations do not push actual data on the stack, but +just move the stack pointer. Local variables on the stack are +represented by integers and accessed by lda_LOCAL <number>, sta_LOCAL +<number>, and similar macros. Parameters passed by sta_PARAM <number> +can be accessed by the callee by lda_LOCAL <number>. Macro CALL may be +used to call a subroutine with parameters. Refer to +sw/stack_test/stack_test.bin and sw/load_from_card/load_from_card.bin +for examples. ** Tools -Directory misc/ contains some tools and unfinished parts: +*** gfs.py + +Program for formatting, listing, and writing SD card in a format +readable by GATE 2010. + +*** boot.py + +Script to upload programs via serial line. + +** Misc *** 6502_monitor/ @@ -73,6 +152,23 @@ A ESP32 monitor for the 65C02. This project has never really been completed, because it would require some level shifter between the 3.3v of the ESP32 and the 5v of the 65C02. +This is a MicroPython script for the ESP32. The pin are connected as +follows: + +- Address Bus (16 pins - only lower 14 connected) +- Data bus (8 pins) +- Clock (1 pin) +- R/W (1 pin) + +Note that ESP32 pins 34 to 39 are read only, pins 6 to 11 (SPI) and 34 +& 36 (UART) cannot be used, thus we have 24 pins available. Therefore +we only connect the lower 14 lines of the address bus. Actually, it +looks like PIN 10 can be used both as input and output. At least the +MCU did not crash ... + +See http://wilsonminesco.com/6502primer/MysteryPins.html and 65c02 +data sheet on how to connect the pins. + *** clock_reset_attiny45/ Generates clocks at different speeds and provides a reset logic. The @@ -80,26 +176,11 @@ clocks have been used in the early implementation phase, before replacing it with a 1 Mhz packaged crystal. The reset logic was replaced by a NE555 configured as a monostable multivibrator. -*** sd_card/ - -So far, the computer system does not have storage. This directory -contains some experiments interfacing the SPI interface of an SD card. - ** TODOs *** Write library functions - Multiplication and division -- Random number generator - CRC generator -- Interrupt based serial I/O - -*** Get some software running - -Candidates: - -- 10PRINT -- MicroChess *** Design PCB -*** Connect SD card diff --git a/doc/notizen.txt b/doc/notizen.txt @@ -1,67 +0,0 @@ -* Design - -See bus_logic/BUS_LOGIC.PLD for memory layout. - - - -http://wilsonminesco.com/6502primer/LogicFamilies.html: "You must have -a way to make sure RAM cannot be written when Φ2 is low!" - -=> Write glue logic for rw: rw_ram = rw_cpu # !phi2 - -If this doesn't solve the problem: - -Alliance RAM data sheet: -"WE#, CE# must be high during all address transitions." -=> Wire both WE#, CE# to PLD. Perhaps add two NAND gates to slow signal down? - - -Allgemein zu Adress-Dekodierung: -http://wilsonminesco.com/6502primer/addr_decoding.html - -Also: Connect DTR to RTS - -* Parts list - -- Renesas RAM (32, 64 K) -- 64 K RAM -- 8, 16 K ROM -- GAL - - -* Links - -http://wilsonminesco.com/6502primer/ -https://www.grappendorf.net/projects/6502-home-computer/ - -* Tools - -** 6502_monitor - -Little tool to control the address and databus of a 6502. This allows -to run the 6502 without any additional peripheral. This is a -MicroPython script for the ESP32. The pin are connected as follows: - -- Address Bus (16 pins - only lower 14 connected) -- Data bus (8 pins) -- Clock (1 pin) -- R/W (1 pin) - -Note that ESP32 pins 34 to 39 are read only, pins 6 to 11 (SPI) and 34 -& 36 (UART) cannot be used, thus we have 24 pins available. Therefore -we only connect the lower 14 lines of the address bus. Actually, it -looks like PIN 10 can be used both as input and output. At least the -MCU did not crash ... - -See http://wilsonminesco.com/6502primer/MysteryPins.html and 65c02 -data sheet on how to connect the pins. - -** mem_monitor - -Little tool to read and write memory. Since we have 24 pins available -on the ESP32, we use one pin for E, and G. A not gate ensure that one -is turned on when the other is turned off. - -Note that IO0 must be pulled up (or at least not down) on power-up in -order to boot into MicroPython. - diff --git a/misc/sd_card/card_info.csv b/misc/sd_card/card_info.csv @@ -1,312 +0,0 @@ -# See -# https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ -# http://www.dejazzer.com/ee379/lecture_notes/lec12_sd_card.pdf -# http://ugweb.cs.ualberta.ca/~c274/resources/hardware/SDcards/SD_SDIO_specsv1.pdf -#https://electronics.stackexchange.com/questions/375357/sd-card-initialization-problem-cmd16-wrong-response -#http://www.rjhcoding.com/avrc-sd-interface-1.php -#http://chlazza.nfshost.com/sdcardinfo.html - - -# Time [s], Analyzer Name, Decoded Protocol Result - -0.000095041666667,SPI,MOSI: 0x74; MISO: 0xFF # CMD52 - IO_RW_DIRECT -0.000115083333333,SPI,MOSI: 0x80; MISO: 0xFF -0.000135083333333,SPI,MOSI: 0x00; MISO: 0xFF -0.000135083333333,SPI,MOSI: 0x00; MISO: 0xFF -0.000155083333333,SPI,MOSI: 0x0C; MISO: 0xFF -0.000175083333333,SPI,MOSI: 0x08; MISO: 0xFF -0.000195083333333,SPI,MOSI: 0x9F; MISO: 0xFF -0.000215083333333,SPI,MOSI: 0xFF; MISO: 0xFF - -0.000235083333333,SPI,MOSI: 0xFF; MISO: 0x04 # Answer - -0.000798041666667,SPI,MOSI: 0x40; MISO: 0xFF # CMD0 - GO_IDLE_STATE -0.000818041666667,SPI,MOSI: 0x00; MISO: 0xFF # Argument 0 -0.000838041666667,SPI,MOSI: 0x00; MISO: 0xFF # Argument 1 -0.000858041666667,SPI,MOSI: 0x00; MISO: 0xFF # Argument 2 -0.000878083333333,SPI,MOSI: 0x00; MISO: 0xFF # Argument 3 -0.000898083333333,SPI,MOSI: 0x95; MISO: 0xFF # CRC -0.000918083333333,SPI,MOSI: 0xFF; MISO: 0xFF # Dummy byte - -0.000938083333333,SPI,MOSI: 0xFF; MISO: 0x01 # Response - Idle - -0.015663291666667,SPI,MOSI: 0x40; MISO: 0xFF # CMD0 - GO_IDLE_STATE -0.015683333333333,SPI,MOSI: 0x00; MISO: 0xFF # Argument 0 -0.015703333333333,SPI,MOSI: 0x00; MISO: 0xFF # Argument 1 -0.015723333333333,SPI,MOSI: 0x00; MISO: 0xFF # Argument 2 -0.015743333333333,SPI,MOSI: 0x00; MISO: 0xFF # Argument 3 -0.015763333333333,SPI,MOSI: 0x95; MISO: 0xFF # CRC -0.015783333333333,SPI,MOSI: 0xFF; MISO: 0xFF # Dummy byte - -0.015803375000000,SPI,MOSI: 0xFF; MISO: 0x01 # Response - Idle - -0.035417000000000,SPI,MOSI: 0x48; MISO: 0xFF # CMD8 - SEND_IF_COND -0.035437000000000,SPI,MOSI: 0x00; MISO: 0xFF # Argument 0 -0.035457041666667,SPI,MOSI: 0x00; MISO: 0xFF # Argument 1 -0.035477041666667,SPI,MOSI: 0x01; MISO: 0xFF # Argument 2 -0.035497041666667,SPI,MOSI: 0xAA; MISO: 0xFF # Argument 3 -0.035517041666667,SPI,MOSI: 0x87; MISO: 0xFF # CRC -0.035537041666667,SPI,MOSI: 0xFF; MISO: 0xFF # Dummy byte - -0.035557041666667,SPI,MOSI: 0xFF; MISO: 0x01 # Response - Idle -0.035577041666667,SPI,MOSI: 0xFF; MISO: 0x00 -0.035597083333333,SPI,MOSI: 0xFF; MISO: 0x00 -0.035617083333333,SPI,MOSI: 0xFF; MISO: 0x01 -0.035637083333333,SPI,MOSI: 0xFF; MISO: 0xAA - -0.035871791666667,SPI,MOSI: 0x45; MISO: 0xFF # CMD5 - IO_SEND_OP_COND -0.035891791666667,SPI,MOSI: 0x00; MISO: 0xFF -0.035911833333333,SPI,MOSI: 0x00; MISO: 0xFF -0.035931833333333,SPI,MOSI: 0x00; MISO: 0xFF -0.035951833333333,SPI,MOSI: 0x00; MISO: 0xFF -0.035971833333333,SPI,MOSI: 0x5B; MISO: 0xFF -0.035991833333333,SPI,MOSI: 0xFF; MISO: 0xFF - -0.036011833333333,SPI,MOSI: 0xFF; MISO: 0x05 - -0.036242375000000,SPI,MOSI: 0x7B; MISO: 0xFF # CMD59 - CRC_ON_OFF -0.036262375000000,SPI,MOSI: 0x00; MISO: 0xFF -0.036282375000000,SPI,MOSI: 0x00; MISO: 0xFF -0.036302375000000,SPI,MOSI: 0x00; MISO: 0xFF -0.036322375000000,SPI,MOSI: 0x01; MISO: 0xFF # CRC on -0.036342416666667,SPI,MOSI: 0x83; MISO: 0xFF -0.036362416666667,SPI,MOSI: 0xFF; MISO: 0xFF - -0.036382416666667,SPI,MOSI: 0xFF; MISO: 0x01 # Response - Idle - -0.036613916666667,SPI,MOSI: 0x77; MISO: 0xFF # CMD 55 - APP_CMD -0.036633916666667,SPI,MOSI: 0x00; MISO: 0xFF -0.036653916666667,SPI,MOSI: 0x00; MISO: 0xFF -0.036673916666667,SPI,MOSI: 0x00; MISO: 0xFF -0.036693916666667,SPI,MOSI: 0x00; MISO: 0xFF -0.036713958333333,SPI,MOSI: 0x65; MISO: 0xFF -0.036733958333333,SPI,MOSI: 0xFF; MISO: 0xFF - -0.036753958333333,SPI,MOSI: 0xFF; MISO: 0x01 # Response - Idle - -0.036955458333333,SPI,MOSI: 0x69; MISO: 0xFF # ACMD41 - SD_SEND_OP_COND -0.036975458333333,SPI,MOSI: 0x40; MISO: 0xFF -0.036995458333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.037015500000000,SPI,MOSI: 0x80; MISO: 0xFF -0.037035500000000,SPI,MOSI: 0x00; MISO: 0xFF -0.037055500000000,SPI,MOSI: 0x17; MISO: 0xFF -0.037075500000000,SPI,MOSI: 0xFF; MISO: 0xFF - -0.037095500000000,SPI,MOSI: 0xFF; MISO: 0x01 # Response - Idle - -0.045396416666667,SPI,MOSI: 0x77; MISO: 0xFF # CMD 55 - APP_CMD -0.045416416666667,SPI,MOSI: 0x00; MISO: 0xFF -0.045436416666667,SPI,MOSI: 0x00; MISO: 0xFF -0.045456416666667,SPI,MOSI: 0x00; MISO: 0xFF -0.045476416666667,SPI,MOSI: 0x00; MISO: 0xFF -0.045496416666667,SPI,MOSI: 0x65; MISO: 0xFF -0.045516416666667,SPI,MOSI: 0xFF; MISO: 0xFF - -0.045536458333333,SPI,MOSI: 0xFF; MISO: 0x01 # Response - Idle - -0.045734000000000,SPI,MOSI: 0x69; MISO: 0xFF # ACMD41 - SD_SEND_OP_COND -0.045754000000000,SPI,MOSI: 0x40; MISO: 0xFF -0.045774000000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.045794000000000,SPI,MOSI: 0x80; MISO: 0xFF -0.045814000000000,SPI,MOSI: 0x00; MISO: 0xFF -0.045834041666667,SPI,MOSI: 0x17; MISO: 0xFF -0.045854041666667,SPI,MOSI: 0xFF; MISO: 0xFF - -0.045874041666667,SPI,MOSI: 0xFF; MISO: 0x00 - -0.046096583333333,SPI,MOSI: 0x7A; MISO: 0xFF # CMD58 - READ_OCR -0.046116583333333,SPI,MOSI: 0x00; MISO: 0xFF -0.046136583333333,SPI,MOSI: 0x00; MISO: 0xFF -0.046156583333333,SPI,MOSI: 0x00; MISO: 0xFF -0.046176625000000,SPI,MOSI: 0x00; MISO: 0xFF -0.046196625000000,SPI,MOSI: 0xFD; MISO: 0xFF -0.046216625000000,SPI,MOSI: 0xFF; MISO: 0xFF - -0.046236625000000,SPI,MOSI: 0xFF; MISO: 0x00 -0.046256625000000,SPI,MOSI: 0xFF; MISO: 0x80 -0.046276625000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.046296625000000,SPI,MOSI: 0xFF; MISO: 0x80 -0.046316666666667,SPI,MOSI: 0xFF; MISO: 0x00 - -0.046575458333333,SPI,MOSI: 0x4A; MISO: 0xFF # CMD10 - SEND_CID -0.046595458333333,SPI,MOSI: 0x00; MISO: 0xFF -0.046615458333333,SPI,MOSI: 0x00; MISO: 0xFF -0.046635458333333,SPI,MOSI: 0x00; MISO: 0xFF -0.046655458333333,SPI,MOSI: 0x00; MISO: 0xFF -0.046675458333333,SPI,MOSI: 0x1B; MISO: 0xFF -0.046695458333333,SPI,MOSI: 0xFF; MISO: 0xFF - -0.046715500000000,SPI,MOSI: 0xFF; MISO: 0x00 -0.046735500000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.046755500000000,SPI,MOSI: 0xFF; MISO: 0xFE -0.046775500000000,SPI,MOSI: 0xFF; MISO: 0x1B -0.046795500000000,SPI,MOSI: 0xFF; MISO: 0x53 -0.046815500000000,SPI,MOSI: 0xFF; MISO: 0x4D -0.046835541666667,SPI,MOSI: 0xFF; MISO: 0x30 -0.046855541666667,SPI,MOSI: 0xFF; MISO: 0x30 -0.046875541666667,SPI,MOSI: 0xFF; MISO: 0x30 -0.047044625000000,SPI,MOSI: 0xFF; MISO: 0x30 -0.047064625000000,SPI,MOSI: 0xFF; MISO: 0x30 -0.047084666666667,SPI,MOSI: 0xFF; MISO: 0x10 -0.047104666666667,SPI,MOSI: 0xFF; MISO: 0x1A -0.047124666666667,SPI,MOSI: 0xFF; MISO: 0x9F -0.047144666666667,SPI,MOSI: 0xFF; MISO: 0x59 -0.047164666666667,SPI,MOSI: 0xFF; MISO: 0x58 -0.047184666666667,SPI,MOSI: 0xFF; MISO: 0x00 -0.047204708333333,SPI,MOSI: 0xFF; MISO: 0xAC -0.047224708333333,SPI,MOSI: 0xFF; MISO: 0xDB -0.047244708333333,SPI,MOSI: 0xFF; MISO: 0x42 -0.047264708333333,SPI,MOSI: 0xFF; MISO: 0x13 - -0.047573750000000,SPI,MOSI: 0x49; MISO: 0xFF # CMD9 - SEND_CSD -0.047593791666667,SPI,MOSI: 0x00; MISO: 0xFF -0.047613791666667,SPI,MOSI: 0x00; MISO: 0xFF -0.047633791666667,SPI,MOSI: 0x00; MISO: 0xFF -0.047653791666667,SPI,MOSI: 0x00; MISO: 0xFF -0.047673791666667,SPI,MOSI: 0xAF; MISO: 0xFF -0.047693791666667,SPI,MOSI: 0xFF; MISO: 0xFF - -0.047713791666667,SPI,MOSI: 0xFF; MISO: 0x00 -0.047733833333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.047753833333333,SPI,MOSI: 0xFF; MISO: 0xFE -0.047773833333333,SPI,MOSI: 0xFF; MISO: 0x00 -0.047793833333333,SPI,MOSI: 0xFF; MISO: 0x7F -0.047813833333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.047833833333333,SPI,MOSI: 0xFF; MISO: 0x32 -0.047853833333333,SPI,MOSI: 0xFF; MISO: 0x5B -0.047873875000000,SPI,MOSI: 0xFF; MISO: 0x5A -0.047968083333333,SPI,MOSI: 0xFF; MISO: 0x83 -0.047988125000000,SPI,MOSI: 0xFF; MISO: 0xBA -0.048008125000000,SPI,MOSI: 0xFF; MISO: 0xF6 -0.048028125000000,SPI,MOSI: 0xFF; MISO: 0xDB -0.048048125000000,SPI,MOSI: 0xFF; MISO: 0xDF -0.048068125000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.048088125000000,SPI,MOSI: 0xFF; MISO: 0x0E -0.048108125000000,SPI,MOSI: 0xFF; MISO: 0x80 -0.048128166666667,SPI,MOSI: 0xFF; MISO: 0x00 -0.048148166666667,SPI,MOSI: 0xFF; MISO: 0xB5 -0.048168166666667,SPI,MOSI: 0xFF; MISO: 0x2F -0.048188166666667,SPI,MOSI: 0xFF; MISO: 0x8B - -0.048452666666667,SPI,MOSI: 0x50; MISO: 0xFF # CMD16 - SET_BLOCKLEN -0.048472666666667,SPI,MOSI: 0x00; MISO: 0xFF -0.048492708333333,SPI,MOSI: 0x00; MISO: 0xFF -0.048512708333333,SPI,MOSI: 0x02; MISO: 0xFF -0.048532708333333,SPI,MOSI: 0x00; MISO: 0xFF -0.048552708333333,SPI,MOSI: 0x15; MISO: 0xFF -0.048572708333333,SPI,MOSI: 0xFF; MISO: 0xFF - -0.048592708333333,SPI,MOSI: 0xFF; MISO: 0x00 - -0.048823166666667,SPI,MOSI: 0x77; MISO: 0xFF # CMD55 - APP_CMD -0.048843166666667,SPI,MOSI: 0x00; MISO: 0xFF -0.048863166666667,SPI,MOSI: 0x00; MISO: 0xFF -0.048883166666667,SPI,MOSI: 0x00; MISO: 0xFF -0.048903166666667,SPI,MOSI: 0x00; MISO: 0xFF -0.048923166666667,SPI,MOSI: 0x65; MISO: 0xFF -0.048943208333333,SPI,MOSI: 0xFF; MISO: 0xFF - -0.048963208333333,SPI,MOSI: 0xFF; MISO: 0x00 - -0.049160750000000,SPI,MOSI: 0x73; MISO: 0xFF # CMD1 - SEND_SCR -0.049180750000000,SPI,MOSI: 0x00; MISO: 0xFF -0.049200750000000,SPI,MOSI: 0x00; MISO: 0xFF -0.049220750000000,SPI,MOSI: 0x00; MISO: 0xFF -0.049240750000000,SPI,MOSI: 0x00; MISO: 0xFF -0.049260750000000,SPI,MOSI: 0xC7; MISO: 0xFF -0.049280791666667,SPI,MOSI: 0xFF; MISO: 0xFF - -0.049300791666667,SPI,MOSI: 0xFF; MISO: 0x00 - -0.049320791666667,SPI,MOSI: 0xFF; MISO: 0xFF -0.049340791666667,SPI,MOSI: 0xFF; MISO: 0xFF -0.049360791666667,SPI,MOSI: 0xFF; MISO: 0xFF -0.049380791666667,SPI,MOSI: 0xFF; MISO: 0xFF -0.049400791666667,SPI,MOSI: 0xFF; MISO: 0xFF -0.049420833333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.049440833333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.049460833333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.049564125000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.049584125000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.049604125000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.049624125000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.049644166666667,SPI,MOSI: 0xFF; MISO: 0xFF -0.049664166666667,SPI,MOSI: 0xFF; MISO: 0xFF -0.049684166666667,SPI,MOSI: 0xFF; MISO: 0xFF -0.049704166666667,SPI,MOSI: 0xFF; MISO: 0xFF -0.049806708333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.049826750000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.049846750000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.049866750000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.049886750000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.049906750000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.049926750000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.049946750000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.050041333333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.050061333333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.050081333333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.050101333333333,SPI,MOSI: 0xFF; MISO: 0xFE -0.050121375000000,SPI,MOSI: 0xFF; MISO: 0x02 -0.050141375000000,SPI,MOSI: 0xFF; MISO: 0x25 -0.050161375000000,SPI,MOSI: 0xFF; MISO: 0x80 -0.050181375000000,SPI,MOSI: 0xFF; MISO: 0x00 -0.050275958333333,SPI,MOSI: 0xFF; MISO: 0x00 -0.050295958333333,SPI,MOSI: 0xFF; MISO: 0x00 -0.050316000000000,SPI,MOSI: 0xFF; MISO: 0x00 -0.050336000000000,SPI,MOSI: 0xFF; MISO: 0x00 -0.050356000000000,SPI,MOSI: 0xFF; MISO: 0x4C -0.050376000000000,SPI,MOSI: 0xFF; MISO: 0xD7 -0.050704541666667,SPI,MOSI: 0x77; MISO: 0xFF -0.050724541666667,SPI,MOSI: 0x00; MISO: 0xFF -0.050744583333333,SPI,MOSI: 0x00; MISO: 0xFF -0.050764583333333,SPI,MOSI: 0x00; MISO: 0xFF -0.050784583333333,SPI,MOSI: 0x00; MISO: 0xFF -0.050804583333333,SPI,MOSI: 0x65; MISO: 0xFF -0.050824583333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.050844583333333,SPI,MOSI: 0xFF; MISO: 0x00 -0.051046083333333,SPI,MOSI: 0x73; MISO: 0xFF -0.051066083333333,SPI,MOSI: 0x00; MISO: 0xFF -0.051086083333333,SPI,MOSI: 0x00; MISO: 0xFF -0.051106125000000,SPI,MOSI: 0x00; MISO: 0xFF -0.051126125000000,SPI,MOSI: 0x00; MISO: 0xFF -0.051146125000000,SPI,MOSI: 0xC7; MISO: 0xFF -0.051166125000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.051186125000000,SPI,MOSI: 0xFF; MISO: 0x00 -0.051206125000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.051226125000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.051246166666667,SPI,MOSI: 0xFF; MISO: 0xFF -0.051266166666667,SPI,MOSI: 0xFF; MISO: 0xFF -0.051286166666667,SPI,MOSI: 0xFF; MISO: 0xFF -0.051306166666667,SPI,MOSI: 0xFF; MISO: 0xFF -0.051326166666667,SPI,MOSI: 0xFF; MISO: 0xFF -0.051346166666667,SPI,MOSI: 0xFF; MISO: 0xFF -0.051441583333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.051461583333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.051481583333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.051501583333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.051521583333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.051541583333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.051561625000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.051581625000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.051676208333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.051696208333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.051716208333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.051736208333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.051756250000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.051776250000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.051796250000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.051816250000000,SPI,MOSI: 0xFF; MISO: 0xFF -0.051910833333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.051930833333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.051950833333333,SPI,MOSI: 0xFF; MISO: 0xFF -0.051970833333333,SPI,MOSI: 0xFF; MISO: 0xFE -0.051990833333333,SPI,MOSI: 0xFF; MISO: 0x02 -0.052010833333333,SPI,MOSI: 0xFF; MISO: 0x25 -0.052030875000000,SPI,MOSI: 0xFF; MISO: 0x80 -0.052050875000000,SPI,MOSI: 0xFF; MISO: 0x00 -0.052145458333333,SPI,MOSI: 0xFF; MISO: 0x00 -0.052165458333333,SPI,MOSI: 0xFF; MISO: 0x00 -0.052185458333333,SPI,MOSI: 0xFF; MISO: 0x00 -0.052205458333333,SPI,MOSI: 0xFF; MISO: 0x00 -0.052225458333333,SPI,MOSI: 0xFF; MISO: 0x4C -0.052245500000000,SPI,MOSI: 0xFF; MISO: 0xD7 diff --git a/misc/sd_card/esp32_sdcard.py b/misc/sd_card/esp32_sdcard.py @@ -1,14 +0,0 @@ -# See -# https://github.com/micropython/micropython/blob/master/drivers/sdcard/sdcard.py -# for protocol - -# Mode = 0 -# CPOL = 0 (low idle) -# CPHA = 0 (first/raising edge) -import machine, uos -c = machine.SDCard(slot=2, sck=18, miso=19, mosi=23, cs=5, freq=2000000) -print(c.info()) -uos.mount(c, "/sd") -uos.listdir('/sd') - -# See card_info.csv for decoded protocol diff --git a/misc/write_default_files.sh b/misc/write_default_files.sh @@ -0,0 +1,11 @@ +#!/bin/sh +GFS_CMD="sudo ../tools/gfs.py" +SW=../sw + +${GFS_CMD} format +${GFS_CMD} store 0 "10print" ${SW}/10print/10print.bin +${GFS_CMD} store 1 "Tic Tac Toe" ${SW}/ttt/ttt.bin +${GFS_CMD} store 2 "Tic Tac Toe (Computer vs. Computer)" ${SW}/ttt/ttt_test.bin +${GFS_CMD} store 3 "Stack Test" ${SW}/stack_test/stack_test.bin +${GFS_CMD} store 4 "Ansi Test" ${SW}/ansi_test/ansi_test.bin +${GFS_CMD} ls diff --git a/roms/boot/ansi.asm b/roms/boot/ansi.asm @@ -0,0 +1,281 @@ +;;; +;;; Special commands for ANSI terminals +;;; + +.namespace export +;;; ANSI text attributes +plain = 0 +bold = 1 +blink = 5 +reverse = 7 + +;;; ANSI colors +black = 0 +red = 1 +green = 2 +yellow = 3 +blue = 4 +magenta = 5 +cyan = 6 +white = 7 +bright_black = 8 +bright_red = 9 +bright_green = 10 +bright_yellow = 11 +bright_blue = 12 +bright_magenta = 13 +bright_cyan = 14 +bright_white = 15 +.endn + +;;; get_screen_size +;;; Returns screen size in X, A. +;;; If not supported by terminal, C is set +;;; Input: +;;; - +;;; Output: +;;; X: Screen width +;;; A: Screen height +;;; Changes: +;;; a, x, y, c +.namespace export +get_screen_size: +.endn + .block + jsr export.create_stack_frame + PUSH $02 + cursor_save_x = 0 ; Local variable + cursor_save_y = 1 ; Local variable + ;; Local variables 0 & 1: Saved cursor position + jsr export.get_cursor ; Save cursor position + bcs terminal_does_not_answer + sta_LOCAL cursor_save_y + txa + sta_LOCAL cursor_save_x + #SET_CURSOR #$FF, #$FF ; Try to move cursor to max. position + jsr export.get_cursor ; Get cursor position (= screen size) + pha ; Save it temporary + txa ; on the + pha ; hardware stack. + lda_LOCAL cursor_save_x ; Restore + tax ; old + lda_LOCAL cursor_save_y ; cursor + jsr export.set_cursor ; position + jsr export.delete_stack_frame + pla + tax + pla + rts +terminal_does_not_answer: + jsr export.delete_stack_frame + ldx #80 + lda #25 + rts + .bend + +.namespace export +clear_screen: +.endn + PRINTS x"1b" .. "[2J" + rts + +.namespace export +set_color: +.endn + pha + PRINTS x"1b" .. "[38;5;" + pla + jsr export.putd + lda #"m" + jmp export.putc + +.namespace export +set_background_color: +.endn + pha + PRINTS x"1b" .. "[48;5;" + pla + jsr export.putd + lda #"m" + jmp export.putc + +.namespace export +set_text: +.endn + tax + lda #"m" + jmp send_CSI + +.namespace export +cursor_up: +.endn + tax + lda #"A" + jmp send_CSI + +.namespace export +cursor_down: +.endn + tax + lda #"B" + jmp send_CSI + +.namespace export +cursor_forward: +.endn + tax + lda #"C" + jmp send_CSI + +.namespace export +cursor_back: +.endn + tax + lda #"D" + jmp send_CSI + +.namespace export +scroll_up: +.endn + tax + lda #"S" + jmp send_CSI + +.namespace export +scroll_down: +.endn + tax + lda #"T" + jmp send_CSI + +;;; Send ANSI escape sequence to terminal. +;;; A contains command, X value +send_CSI: + pha + phx + lda #$1b + jsr export.putc + lda #"[" + jsr export.putc + pla + jsr export.putd + pla + jmp export.putc + +;;; set_cursor +;;; Sets cursor to position x, a. +;;; Position starts at 1, not 0! +;;; Input: +;;; x: x position +;;; a: y position +;;; Output: +;;; - +;;; Changes: +;;; a, x, y, c +;;; Set Cursor; x position in X, y position in A +.namespace export +set_cursor: +.endn + phx + pha + lda #$1b + jsr export.putc + lda #"[" + jsr export.putc + pla + jsr export.putd + lda #";" + jsr export.putc + pla + jsr export.putd + lda #"H" + jmp export.putc + +;;; get_cursor +;;; Store cursor position in x, a. +;;; Position starts at 1, not 0! +;;; Input: +;;; - +;;; Output: +;;; x: x position +;;; a: y position +;;; Changes: +;;; a, x, y, c +.namespace export +get_cursor: +.endn + .block + jsr export.create_stack_frame + PUSH $08 ; Position as string + ;; We store the position string as local variables. + ;; By initializing the local variables with #$00, + ;; the strings terminate automatically when no + ;; more digits are written. + lda #$00 + .for i = 0, i < 8, i += 1 + sta_LOCAL i + .next + ;; Request cursor position + PRINTS x"1b" .. "[6n" + ;; Read answer + ldx #$08 ; Number of tries + ldy #$ff ; to get an answer. +wait_for_answer: + phx + phy + jsr export.getc_nonblocking ; $1b + ply + plx + bcc terminal_answers + dey + cpy #$00 + bne wait_for_answer + dex + cpx #$00 + bne wait_for_answer + ;; Terminal does not answer. + jsr export.delete_stack_frame + lda #$00 + ldx #$00 + sec + rts +terminal_answers: + jsr export.getc ; "[" + jsr export.getc ; First digit y + sta_LOCAL 0 + jsr export.getc ; Second digit y + cmp #";" + beq got_y_position + sta_LOCAL 1 + jsr export.getc ; Third digit y + cmp #";" + beq got_y_position + sta_LOCAL 2 +got_y_position: + jsr export.getc ; First digit x + sta_LOCAL 4 + jsr export.getc ; Second digit x + cmp #"R" + beq got_x_position + sta_LOCAL 5 + jsr export.getc ; Third digit x + cmp #"R" + beq got_x_position + sta_LOCAL 6 +got_x_position: + ;; x position is in local variable(s) 4... + ;; y position is in local variable(s) 0... + ;; Convert them to ints + CALL export.str2byte, [0, 1, 2, 3], lda_LOCAL + lda_PARAM 0 + pha + CALL export.str2byte, [4, 5, 6, 7], lda_LOCAL + lda_PARAM 0 + pha + jsr export.delete_stack_frame + plx + pla + clc + rts + .bend + diff --git a/roms/boot/ansi.inc b/roms/boot/ansi.inc @@ -0,0 +1,59 @@ +;;; -*- asm -*- + +;;; ----------------------------------- +;;; +;;; ANSI Terminal +;;; +;;; ----------------------------------- + +SET_COLOR .macro color + lda #color + jsr set_color + .endm + +SET_BACKGROUND_COLOR .macro color + lda #color + jsr set_background_color + .endm + +SET_TEXT .macro attribute + lda #\attribute + jsr set_text + .endm + +CURSOR_UP .macro steps + lda #\steps + jsr cursor_up + .endm + +CURSOR_DOWN .macro steps + lda #\steps + jsr cursor_down + .endm + +CURSOR_FORWARD .macro steps + lda #\steps + jsr cursor_forward + .endm + +CURSOR_BACK .macro steps + lda #\steps + jsr cursor_back + .endm + +SCROLL_UP .macro steps + lda #\steps + jsr scroll_up + .endm + +SCROLL_DOWN .macro steps + lda #\steps + jsr scroll_DOWN + .endm + +SET_CURSOR .macro x, y + lda \y + ldx \x + jsr export.set_cursor + .endm + diff --git a/roms/boot/boot.asm b/roms/boot/boot.asm @@ -10,7 +10,7 @@ rom_start = $e000 .endif -.include "boot_macros.inc" +.include "boot.inc" * = $00 .dsection zero_page @@ -107,7 +107,7 @@ got_byte: download_program: .block ;; Program is loaded to this memory address - start_addr = $0300 + start_addr = $0200 ;; Location of temporary variables addr_pointer = $12 ; addr_pointer+1 stores MSB msb_last_addr = $14 @@ -163,398 +163,17 @@ transmit_block: lda xor_checksum jsr export.putc ;; Start program - jmp $0300 + jmp $0200 .bend -.namespace export -.if SYMON - acia_base = $8800 ; Symon -.else - acia_base = $dc00 -.endif - acia_data_reg = acia_base - acia_status_reg = acia_base + $1 - acia_cmd_reg = acia_base + $2 - acia_ctrl_reg = acia_base + $3 -.endn - -;;; init_acia -;;; Initialize acai for communication -;;; Input: -;;; - -;;; Output: -;;; - -;;; Changes: -;;; a, acai registers -.namespace export -init_acia: -.endn - .block - ;; Reset acai - sta export.acia_status_reg - lda #%00011111 ; 19200 bps, 8 data bits, 1 stop bit - sta export.acia_ctrl_reg - ;; No parity, no echo, no interrupts, DTR ready - lda #%11001011 - sta export.acia_cmd_reg - rts - .bend - -.namespace export -.if SYMON - via_start = $8000 -.else - via_start = $d800 -.endif - via_irb = via_start - via_orb = via_start - via_ira = via_start+$1 - via_ora = via_start+$1 - via_ddrb = via_start+$2 - via_ddra = via_start+$3 - via_t1cl = via_start+$4 - via_t1ch = via_start+$5 - via_t1ll = via_start+$6 - via_t1lh = via_start+$7 - via_t2cl = via_start+$8 - via_t2ch = via_start+$9 - via_sr = via_start+$10 - via_acr = via_start+$11 - via_pcr = via_start+$12 - via_ifr = via_start+$13 - via_ier = via_start+$14 - via_ira2 = via_start+$15 - via_ora2 = via_start+$15 -.endn - -;;; ---------------------------------------------------------- -;;; STANDARD LIBRARY FUNCTIONS -;;; -;;; These functions may be used by user programs. Labels -;;; marked with 'EXPORT' are exported to liba.h. When -;;; the user program imports this header file, the functions -;;; are available. -;;; ---------------------------------------------------------- - -;;; puts -;;; Send up to 256 characters terminated by null via acia -;;; Input: -;;; puts_str, put_str+1: -;;; 2 bytes on zero page containing string address -;;; Output: -;;; - -;;; Changes: -;;; a, x, y, puts_str, put_str+1 -.namespace export -puts: -.endn - .block - ;; Send string terminated by '\0' - ldy #$00 -_puts_loop: - lda (export.puts_str),y - beq _puts_end - phy - jsr export.putc - ply - iny - jmp _puts_loop -_puts_end: - rts - .bend +.include "io.asm" +.include "stack.asm" +.include "lfsr.asm" +.include "spi.asm" +.include "sd_card.asm" +.include "ansi.asm" -;;; putc -;;; Send character via acia -;;; Input: -;;; a: -;;; Character to be printed -;;; Output: -;;; - -;;; Changes: -;;; x, acai registers -.namespace export -putc: -.endn - .block - ;; Send character. - ;; Due to bugs in the register and interrupt - ;; handling of the WDC 65C02, we have to use - ;; a manual delay. - sta export.acia_data_reg -.switch CLOCK_SPEED -.case 4 ; 4 Mhz Clock - SERIAL_SEND_DELAY_X = $c3 - SERIAL_SEND_DELAY_Y = $02 -.case 2 ; 4 Mhz Clock - SERIAL_SEND_DELAY_X = $c2 - SERIAL_SEND_DELAY_Y = $01 -.default ; 1 Mhz Clock - SERIAL_SEND_DELAY_X = $88 - SERIAL_SEND_DELAY_Y = $01 -.endswitch - ldy #SERIAL_SEND_DELAY_Y -outer_loop: - ldx #SERIAL_SEND_DELAY_X -inner_loop: - dex - bne inner_loop - dey - bne outer_loop - rts - .bend - -;;; puth -;;; Convert byte to hex and send via acia -;;; Input: -;;; a: -;;; Byte to be printed -;;; Output: -;;; - -;;; Changes: -;;; a, x, acai registers, c-flag -.namespace export -puth: -.endn - .block - ;; Send byte a as hex number - pha - lsr a - lsr a - lsr a - lsr a - jsr export.puth_nibble - pla - jsr export.puth_nibble - rts - .bend - -;;; puth_nibble -;;; Convert lower half of byte to hex and send via acia -;;; Input: -;;; a: -;;; Nibble to be printed -;;; Output: -;;; - -;;; Changes: -;;; a, x, acai registers, c-flag -.namespace export -puth_nibble: -.endn - .block - ;; Print hex digit - clc - and #$0F - adc #$30 ; Decimal number - cmp #$3A ; >10 ? - bmi _puth_putc - adc #$26 -_puth_putc: - jsr export.putc - rts - .bend - -;;; putnl -;;; Send newline via acia -;;; Input: -;;; - -;;; Output: -;;; - -;;; Changes: -;;; a -.namespace export -putnl: -.endn - .block - lda #$0d - jsr export.putc - lda #$0a - jsr export.putc - rts - .bend - -;;; getc -;;; Read character from acia. Blocks until character is read -;;; Input: -;;; - -;;; Output: -;;; a: -;;; Character read -;;; Changes: -;;; a, acai registers -.namespace export -getc: -.endn - .block - ;; Read character from acia - lda export.acia_status_reg - and #%00001000 - beq export.getc - lda export.acia_data_reg - rts - .bend - -;;; getc_seed_rng -;;; Like getc, but also updates lsfr -;;; This is our only source of randomness right now: -;;; While we are busy waiting for a character, lsfr is -;;; updated. -;;; Input: -;;; - -;;; Output: -;;; a: -;;; Character read -;;; Changes: -;;; a, acai registers, lsfr_state -.namespace export -getc_seed_rng: -.endn - .block - ;; Read character from acia - ;; We also use the time between keystrokes - ;; as entropy source for the RNG - jsr export.lfsr_step - lda export.acia_status_reg - and #%00001000 - beq export.getc_seed_rng - lda export.acia_data_reg - rts - .bend - -.namespace export -getc_nonblocking: -.endn - .block - ;; Non-blocking read: If - ;; character has been read, - ;; it is returned in A and - ;; C is cleared. If not, - ;; C is set - sec - lda export.acia_status_reg - and #%00001000 - beq nothing_read - clc - lda export.acia_data_reg -nothing_read: - rts - .bend - -;;; gets -;;; Read up to 255 characters terminated by CR via acia -;;; Input: -;;; gets_len: -;;; Max. length of input. -;;; gets_str, gets_str+1: -;;; 2 bytes on zero page containing destination address -;;; +-------------------------------------------------------+ -;;; |IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! | -;;; +-------------------------------------------------------+ -;;; Note that a terminating zero is added to the string. -;;; Therefore gets_len+1 bytes may be written! -;;; Output: -;;; Zero terminated string at gets_str, gets_str+1 -;;; Changes: -;;; a, y, ACAI registers -.namespace export -gets: -.endn - .block - ;; Read string terminated by CR - ldy #$00 -loop: - phy - jsr export.getc - jsr export.putc - ply - cmp #$0d ; GOT CR? - beq terminate_string ; if not - sta (export.gets_str), y ; store character - iny ; if max length - cpy export.gets_len ; not reached, - bne loop ; continue loop. -terminate_string: - lda #$00 - sta (export.gets_str), y - rts - .bend - -;;; Initialize VIA: Switch all ports to output -;;; and all pins to low -.namespace export -init_via: - lda #$ff - sta export.via_ddra - sta export.via_ddrb - lda #$00 - sta export.via_ora - sta export.via_orb - rts -.endn - - -;;; 16 Bit Galouis LFSR -;;; If you use the whole state as RNG, the quality of the -;;; random numbers is rather poor. Just extracting one -;;; bit from the state yields random numbers with reasonable -;;; statistical properties. - -;;; lfsr_init -;;; Initialize lfsr -;;; Input: -;;; - -;;; Output: -;;; - -;;; Changes: -;;; lfsr -.namespace export -lfsr_init: -.endn - .block - #STORE_WORD $beef, export.lfsr_state - .bend - -;;; lfsr_step -;;; update lfsr -;;; Input: -;;; - -;;; Output: -;;; - -;;; Changes: -;;; lfsr -.namespace export -lfsr_step: -.endn - .block - asl export.lfsr_state - lda export.lfsr_state+1 - rol a - bcc cont - eor #$0b -cont: - sta export.lfsr_state+1 - lda export.lfsr_state - adc #$00 - sta export.lfsr_state - rts - .bend - -.if false -;;; Test function for LFSR -test_lfsr: .block -loop: - jsr export.lfsr_step - lda export.lfsr_state+1 - jsr puth - lda export.lfsr_state - jsr export.puth - PRINTSNL("") - jsr export.getc - jmp loop - .bend -.endif ; false - - ;;; Default IRQ handler. Unless the user program changes ;;; the irq_vector, IRQs are handled here. .namespace export @@ -567,435 +186,6 @@ derefer_ram_irq: ;; in RAM jmp (export.irq_vector) -;;; ---------------------------------------------------------- -;;; -;;; STACK -;;; -;;; ---------------------------------------------------------- - -;;; Software implementation of data stack. In difference to typical -;;; stack implementations, this stack grows from bottom to top, because -;;; we want to access eleements using indirected indexed access with -;;; the stack frame pointer as base. Therefore the stack should -;;; be initialized with the first unoccupied RAM address after the -;;; program code. -;;; -;;; PULL and PUSH do not move actual stack data, they just shift -;;; the stack pointer. -;;; -;;; Local variables on the data stack are supported by macro -;;; VAR. VAR i refers to the ith byte on the stack. PARAM allows -;;; to pass variables to a callee. PARAM i of the caller becomes -;;; VAR i of the callee. The callee should reserve stack space -;;; for both its parameters and local variables. -;;; -;;; Stack frames used for these callse are created/destroyed by -;;; subroutines new_stack_frame and remove_stack_frame. It is -;;; recommended to use macros CALL and RETURN instead of calling -;;; new_stack_frame and remove_stack_frame directly. -;;; -;;; CALL is like jsr with parameter passing. There addressing -;;; modes are supported: immediate, zero-page, and local variables. -;;; For immediate and zero page, the third parameter must be lda. -;;; For local variables, the third parameter must be lda_LOCAL. -;;; See sw/stack_test/stack_test.asm for examles. - - -.namespace export -new_stack_frame: -.endn - .block - ;; Store old frame pointer on stack. - ;; Since our stack grows bottom to top, we write - ;; the data before increasing the stack pointer - #COPY_WORD_ABSOLUTE_INDIRECT export.ds_frame, (export.ds_pointer) - #PUSH $02 ; Store old frame pointer - #COPY_WORD_ABSOLUTE_INDIRECT export.ds_pointer, export.ds_frame ; Set new frame pointer - rts - .bend - -.namespace export -remove_stack_frame: -.endn - .block - #COPY_WORD_ABSOLUTE_INDIRECT export.ds_frame, export.ds_pointer ; Reset stack pointer to begin of frame. - #PULL $02 ; Move stack pointer to frame reference - #COPY_WORD_ABSOLUTE_INDIRECT (export.ds_pointer), export.ds_frame ; Restore old frame pointer. - rts - .bend - -.namespace export -push_a: -.endn - .block - sta (export.ds_pointer) - #PUSH $01 - rts - .bend - -.namespace export -pull_a: -.endn - .block - #PULL $01 - lda (export.ds_pointer) - rts - .bend - -;;; ----------------------------------- -;;; -;;; SPI read and write operations -;;; -;;; ----------------------------------- - - ;; Wiring: - ;; PA0: CS - CS = %00000001 - ;; PA1: SCK - SCK = %00000010 - ;; PA2: MOSI - MOSI = %00000100 - ;; PA3: MISO - MISO = %00001000 - - -;;; Blocking read - read until -;;; byte != $ff is read or retry counter -;;; expires. -.namespace export -spi_get: -.endn - .block - ldy #$00 ; Try up to $ff times -retry_loop: - jsr export.spi_get_nonblocking - ;; If we read $ff, we did not receive - ;; a byte. - cmp #$ff - bne done - dey ; Try again unless retry - bne retry_loop ; counter expired. -done: - CLEAR_BITS export.via_ora, #(MOSI|SCK) ; Set MOSI and SCK low on completion. - lda export.via_buffer - rts - .bend - -;;; Return next byte; may be $ff in case of no transmission -.namespace export -spi_get_nonblocking: -.endn - SET_BITS export.via_ora, #MOSI ; Keep MOSI high during read - lda #$00 ; Clear - sta export.via_buffer ; receive buffer. - ldx #$08 ; 8 bit to read. -read_bits_loop: - .block - asl export.via_buffer ; Make room for next bit - CLEAR_BITS export.via_ora, #SCK ; Generate clock impulse - SET_BITS export.via_ora, #SCK - lda export.via_ira ; Get next bit. - and #MISO - cmp #MISO - bne cont ; Nothing to do if we got a zero bit. - inc export.via_buffer ; Set lowest bit in via_buffer -cont: - dex - bne read_bits_loop - CLEAR_BITS export.via_ora, #(MOSI|SCK) ; Set MOSI and SCK low on completion. - lda export.via_buffer - rts - .bend - - - -;;; Write byte via SPI -.namespace export -spi_set: -.endn - .block - sta export.via_buffer ; We send from via_buffer - ldx #$08 ; 8 bit to write -write_bits_loop: - CLEAR_BITS export.via_ora, #SCK ; Set bits on low clock - CLEAR_BITS export.via_ora,#MOSI - lda export.via_buffer ; Get next bit to send - and #%10000000 ; Isolate bit to send. - beq cont ; Set MOSI accordingly. - SET_BITS export.via_ora,#MOSI - jmp cont -cont: - SET_BITS export.via_ora, #SCK ; Send bits on high clock - asl export.via_buffer ; Advance to next bit - dex - bne write_bits_loop - CLEAR_BITS export.via_ora,#SCK - rts - .bend - -;;; ----------------------------------- -;;; -;;; SD card communication interface -;;; -;;; ----------------------------------- - - -;;; Send command -.namespace export -sd_send_cmd: -.endn - .block - ldy #$00 -cmd_bytes_loop: - lda (export.sd_data),y - jsr export.spi_set - iny - cpy #$06 - bne cmd_bytes_loop - lda #$ff ; Finalize transmission - jsr export.spi_set ; with dummy byte - rts - .bend - - -.namespace export -sd_open: -.endn - .block -.if DEBUG - PRINTSNL "Initializing SD Card" -.endif - ;; Configure ports for in- and output - SET_BITS export.via_ddra, #(CS | SCK | MOSI) - CLEAR_BITS export.via_ddra, #MISO - ldy #$10 ; Error counter - phy -start_init: - ;; Card power on sequence: - ;; Keep CS high, toggle clock for at least 74 cycles. - SET_BITS export.via_ora, #(CS | MOSI) - ;; Wait for 80 clock pulses - ldx #160 -boot_wait_loop: - TOGGLE_BITS export.via_ora, #SCK - dex - bne boot_wait_loop - CLEAR_BITS export.via_ora, #(CS|MOSI|SCK) - SD_SEND_CMD [$40, $00, $00, $00, $00, $95] ; CMD0 - SD_RECEIVE $1 - cmp #$01 ; Should be $01 (In idle state). - beq cont - ply - dey - phy - beq failed_init - jmp start_init ; If not, start over. -failed_init: - ply - PRINTSNL("Error initializing SD card!") - sec - rts -cont: - ;; Card is in SPI mode - ply - SD_SEND_CMD [$48, $00, $00, $01, $aa, $87] ; CMD8 - SD_RECEIVE $1 - SD_SEND_CMD [$45, $00, $00, $00, $00, $5b] ; CMD5 - SD_RECEIVE $1 ; $05 = SD; $c1 = SDHC - sta export.sd_type - SD_SEND_CMD [$7b, $00, $00, $00, $00, $83] ; CMD59 - SD_RECEIVE $1 ; should be $01 - ;; Send ACMD41 until card is initialized - ldy #$10 ; Fail counter - phy -wait_for_card_initialized: - SD_SEND_CMD [$77, $00, $00, $00, $00, $65] ; CMD55 - SD_RECEIVE $1 ; should be $01 - SD_SEND_CMD [$69, $40, $00, $00, $00, $95] ; ACMD41 (v1 & v2 card) - SD_RECEIVE $1 ; $01 if busy, $00 if ready (initialized) - cmp #$00 - beq initialized - ply - dey - phy - bne cont2 ; More tries? -cont2: - jmp wait_for_card_initialized -initialized: - ply - SD_SEND_CMD [$50, $00, $00, $02, $00, $15] ; CMD16 - SD_RECEIVE $1 - clc - rts - .bend - - -.namespace export -print_block: -.endn - .block - ldx #$02 - ldy #$00 -loop: - phx - phy - lda (export.sd_data),y - jsr export.puth - PRINTS " " - ply - plx - iny - bne loop - ADD_WORD export.sd_data, $0100 - dex - bne loop - PRINTNL - SUB_WORD export.sd_data, $0200 - rts - .bend - -.namespace export -sd_read_block: -.endn - ;; Read a block of data - ;; Block number is passed via stack. - .block -.if DEBUG - PRINTSNL "Sending CMD17" -.endif - PUSH $04 ; Parameters (block number) - lda #$51 ; CMD17 (READ_SINGLE_BLOCK) - jsr export.spi_set - jsr sd_send_block_number - lda #$01 ; Dummy CRC - jsr export.spi_set - jsr export.spi_get - cmp #$00 - beq wait_for_start_token - jmp read_failed -wait_for_start_token: - jsr export.spi_get_nonblocking - cmp #$ff - beq wait_for_start_token - cmp #$fe - bne read_failed - ldx #$02 - ldy #$00 -loop: - phx - phy - jsr export.spi_get_nonblocking - ply - plx - sta (export.sd_data),y - iny - bne loop - ADD_WORD export.sd_data, $0100 ; Second half of block - dex - bne loop - SUB_WORD export.sd_data, $0200 ; Restore pointer - clc - SD_RECEIVE $2 ; Get CRC - RETURN -read_failed: - PRINTSNL("Error reading SD card!") - sec - RETURN - .bend - -sd_send_block_number: - .block - lda export.sd_type ; Old cards expect the position - cmp #$05 ; as bytes, not blocks - beq old_card - lda_LOCAL 0 ; Block number - jsr export.spi_set - lda_LOCAL 1 - jsr export.spi_set - lda_LOCAL 2 - jsr export.spi_set - lda_LOCAL 3 - jsr export.spi_set - rts -old_card: - lda_LOCAL 3 - asl a - sta_LOCAL 3 - lda_LOCAL 2 - rol a - sta_LOCAL 2 - lda_LOCAL 1 - rol a - jsr export.spi_set - lda_LOCAL 2 - jsr export.spi_set - lda_LOCAL 3 - jsr export.spi_set - lda #$00 - jsr export.spi_set - rts - .bend - -.namespace export -sd_write_block: -.endn - ;; Write block at sd_data to card - ;; Block number is passed via stack - .block -.if DEBUG - PRINTSNL "Sending CMD24" -.endif - PUSH $04 ; Parameters (block number) - lda #$58 ; CMD24 (WRITE_BLOCK) - jsr export.spi_set - jsr sd_send_block_number - lda #$01 ; Dummy CRC - jsr export.spi_set - lda #$ff - jsr export.spi_set ; Dummy byte - SD_RECEIVE $1 - ;; Send start token - lda #$fe - jsr export.spi_set - ;; Send data - ldx #$02 - ldy #$00 -write_loop: - phx - phy - lda (export.sd_data),y - jsr export.spi_set - ply - plx - iny - cpy #$00 - bne write_loop - ADD_WORD export.sd_data, $0100 ; Second half of block - dex - cpx #$00 - bne write_loop - SUB_WORD export.sd_data, $0200 ; Restore pointer - ;; Wait for write operation to complete - SD_RECEIVE $1 - cmp #$e5 - bne error_writing -wait_loop: - SD_RECEIVE $1 - beq wait_loop - clc - RETURN -error_writing: - PRINTSNL("Error writing to SD card!") - sec - RETURN - .bend - -sd_close: - #SET_BITS export.via_ora, #CS - rts - - - ;;; Default NMI handler. Unless the user program changes ;;; the nmi_vector, NMIs are handled here. .namespace export @@ -1011,12 +201,12 @@ derefer_ram_nmi: default_program: ;; .include "../../sw/10print/10print.asm" ;; .include "../../sw/ttt/ttt.asm" -.include "../../sw/sd_card/sd_card.asm" +.include "../../sw/load_from_card/load_from_card.asm" ;;; Vectors .fill $fffa-*, $ff .word derefer_ram_nmi ; nmi - .word boot ; reset - .word derefer_ram_irq ; irq + .word boot ; reset + .word derefer_ram_irq ; irq .send rom diff --git a/roms/boot/boot.inc b/roms/boot/boot.inc @@ -0,0 +1,117 @@ +;;; -*- asm -*- + +;;; This file may be included from boot.asm or from +;;; some userland program. If included from a +;;; boot.asm, external symbols are defined in +;;; namespace export. We define this namespace here +;;; in case this file is included from a userland program. +;;; We use the definition of CLOCK_SPPED in boot.asm to +;;; check if we are compiling in the context of a userland +;;; program. +.weak + CLOCK_SPEED = 0 +.endweak +.if CLOCK_SPEED == 0 + export .namespace + puts = puts + puth = puth + puts_str = puts_str + gets_str = gets_str + gets_len = gets_len + gets = gets + putc = putc + putd = putd + create_stack_frame = create_stack_frame + delete_stack_frame = delete_stack_frame + push_a = push_a + pull_a = pull_a + ds_pointer = ds_pointer + ds_frame = ds_frame + ram_end = ram_end + set_cursor = set_cursor + .endn + BOOT_EMBEDDED = false +.else + BOOT_EMBEDDED = true +.endif + +.include "io.inc" +.include "stack.inc" +.include "sd_card.inc" +.include "ansi.inc" + +;;; ----------------------------------- +;;; +;;; Miscellaneous utility functions +;;; +;;; ----------------------------------- + +SET_BITS .macro + lda \1 + ora \2 + sta \1 + .endm + +CLEAR_BITS .macro + lda \1 + and ~\2 + sta \1 + .endm + +TOGGLE_BITS .macro + lda \1 + eor \2 + sta \1 + .endm + +;;; STORE_WORD(word,dst) +;;; Store word at dst +;;; Input: +;;; word +;;; Output: +;;; - +;;; Changes: +;;; a, dst +STORE_WORD .macro + lda #<\1 + sta \2 + lda #>\1 + sta \2+1 + .endm + + +COPY_WORD_IMMEDIATE: .macro + lda #<\1 + sta \2 + lda #>\1 + sta \2+1 + .endm + + ;; Copy macro for absolute and indirect addressing modes +COPY_WORD_ABSOLUTE_INDIRECT: .macro + lda \1 + sta \2 + ldy #$01 + lda \1,y + sta \2,y + .endm + +SUB_WORD: .macro + lda \1 + sec + sbc #<\2 + sta \1 + lda \1+1 + sbc #>\2 + sta \1+1 + .endm + +ADD_WORD: .macro + lda \1 + clc + adc #<\2 + sta \1 + lda \1+1 + adc #>\2 + sta \1+1 + .endm diff --git a/roms/boot/boot_macros.inc b/roms/boot/boot_macros.inc @@ -1,339 +0,0 @@ -;;; -*- asm -*- - -;;; This file may be included from boot.asm or from -;;; some userland program. If included from a -;;; boot.asm, external symbols are defined in -;;; namespace export. We define this namespace here -;;; in case this file is included from a userland program. -;;; We use the definition of CLOCK_SPPED in boot.asm to -;;; check if we are compiling in the context of a userland -;;; program. -.weak - CLOCK_SPEED = 0 -.endweak -.if CLOCK_SPEED == 0 - export .namespace - puts = puts - puth = puth - puts_str = puts_str - gets_str = gets_str - gets_len = gets_len - gets = gets - putc = putc - new_stack_frame = new_stack_frame - remove_stack_frame = remove_stack_frame - push_a = push_a - pull_a = pull_a - ds_pointer = ds_pointer - ds_frame = ds_frame - ram_end = ram_end - .endn - BOOT_EMBEDDED = false -.else - BOOT_EMBEDDED = true -.endif - -;;; PRINT(addr) -;;; Send zero terminated string at addr via acia. -;;; Input: -;;; addr: -;;; Address of zero terminated string (<= 256 characters) -;;; Output: -;;; - -;;; Changes: -;;; a, x, y, puts_str, put_str+1 - -PRINT .macro - lda #<\1 - sta export.puts_str - lda #>\1 - sta export.puts_str+1 - jsr export.puts - .endm - -;;; PRINTS(string) (<= 255 characters) -;;; Send string via acia -;;; Input: -;;; string: -;;; String to be send. -;;; Output: -;;; - -;;; Changes: -;;; a, x, y, puts_str, put_str+1 - -PRINTS .macro - PRINT(saddr) - jmp cont_PRINTS -saddr: - .null \1 -cont_PRINTS: - .endm - -;;; PRINTSNL(string) (<= 253 characters) -;;; Send string and newline via acia -;;; Input: -;;; string: -;;; String to be send. -;;; Output: -;;; - -;;; Changes: -;;; a, x, y, puts_str, put_str+1 - -PRINTSNL .macro - PRINT(saddr) - jmp cont_PRINTSNL -saddr: - .null \1, $0d, $0a -cont_PRINTSNL: - .endm - -;;; INPUTS(addr, len) -;;; Read up to len characters from acai and store them at addr -;;; This is a wrapper for gets -;;; Input: -;;; addr: -;;; Target address for zero-terminated string -;;; len: -;;; Maximal length of string to be read (not terminating zero!) -;;; +-------------------------------------------------------+ -;;; |IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! | -;;; +-------------------------------------------------------+ -;;; Note that a terminating zero is added to the string. -;;; Therefore len+1 bytes may be written! -;;; Output: -;;; - -;;; Changes: -;;; a, y, ACAI registers - -INPUTS .macro - lda #<\1 - sta export.gets_str - lda #>\1 - sta export.gets_str+1 - lda \2 - sta export.gets_len - jsr export.gets - .endm - -;;; PRINTNL -;;; Send newline via acia -;;; Input: -;;; - -;;; Output: -;;; - -;;; Changes: -;;; a, x, acai registers - -PRINTNL .macro - lda #$0d - jsr export.putc - lda #$0a - jsr export.putc - .endm - -;;; STORE_WORD(word,dst) -;;; Store word at dst -;;; Input: -;;; word -;;; Output: -;;; - -;;; Changes: -;;; a, dst - - -STORE_WORD .macro - lda #<\1 - sta \2 - lda #>\1 - sta \2+1 - .endm - - -;;; Macros for accessing the data stack -;;; and managing local variables and -;;; call parameters. Check boot.asm for -;;; documentation. - -;;; Stack related macros - -INIT_STACK: .macro - #STORE_WORD \1, export.ds_pointer - .endm - -PUSH: .macro ; Reserve bytes on stack (no actual push operation) - #ADD_WORD export.ds_pointer, \1 - .endm - -PULL: .macro ; Remove bytes from stack (no return value) - #SUB_WORD export.ds_pointer, \1 - .endm - -PUSH_WORD: .macro ; Push a word on the stack - lda #<\1 - sta (export.ds_pointer) - lda #>\1 - ldy #$01 - sta (export.ds_pointer),y - #PUSH $02 - .endm - -COPY_WORD_IMMEDIATE: .macro - lda #<\1 - sta \2 - lda #>\1 - sta \2+1 - .endm - - ;; Copy macro for absolute and indirect addressing modes -COPY_WORD_ABSOLUTE_INDIRECT: .macro - lda \1 - sta \2 - ldy #$01 - lda \1,y - sta \2,y - .endm - - ;; Push sequence of bytes to stack. Parameters: From, Length -PUSH_RANGE: .macro - ldy #$00 -loop_PUSH_RANGE: - lda \1,y - sta (ds_pointer),y - iny - cpy \2 - bne loop_PUSH_RANGE - #PUSH \2 - .endm - -SUB_WORD: .macro - lda \1 - sec - sbc #<\2 - sta \1 - lda \1+1 - sbc #>\2 - sta \1+1 - .endm - -ADD_WORD: .macro - lda \1 - clc - adc #<\2 - sta \1 - lda \1+1 - adc #>\2 - sta \1+1 - .endm - - -;;; Local variables & call parameters related macros - -LOCAL: .macro - ldy #\1 - \2 (export.ds_frame),y - .endm -lda_LOCAL: .macro - LOCAL \1, lda - .endm -sta_LOCAL: .macro - LOCAL \1, sta - .endm -adc_LOCAL: .macro - LOCAL \1, adc - .endm -PARAM: .macro - ldy #\1 - \2 (ds_pointer),y - .endm -sta_PARAM: .macro - PARAM \1+2, sta - .endm -lda_PARAM: .macro - PARAM \1+2, lda - .endm - -CALL: .macro - .for i := 0, i < len(\2), i += $1 - \3 \2[i] - sta_PARAM i - .next - jsr new_stack_frame - jsr \1 - .endm - -RETURN: .macro - jsr export.remove_stack_frame - rts - .endm - - ;; Macros for accessing SD cards - -SD_SEND_CMD .macro -.if DEBUG - PRINTS "Sending CMD$" - lda cmd - sec - sbc #$40 - jsr export.puth - PRINTNL -.endif - lda #<cmd - sta export.sd_data - lda #>cmd - sta export.sd_data+1 - jsr export.sd_send_cmd - jmp cont_SD_SEND_CMD -cmd: .byte \1 -cont_SD_SEND_CMD: - .endm - - - -SD_RECEIVE .macro - ldx #\1 -loop_SD_RECEIVE: - phx - jsr export.spi_get -.if DEBUG - pha - jsr export.puth - PRINTS " " - pla -.endif - plx - dex - bne loop_SD_RECEIVE - pha -.if DEBUG - PRINTNL -.endif - ;; Read non-existing dummy byte for snychronization - jsr export.spi_get_nonblocking - pla - .endm - -;;; ----------------------------------- -;;; -;;; Macros for bit manipulation -;;; -;;; ----------------------------------- - -SET_BITS .macro - lda \1 - ora \2 - sta \1 - .endm - -CLEAR_BITS .macro - lda \1 - and ~\2 - sta \1 - .endm - -TOGGLE_BITS .macro - lda \1 - eor \2 - sta \1 - .endm - - diff --git a/roms/boot/io.asm b/roms/boot/io.asm @@ -0,0 +1,424 @@ +;;; Serial IO and string manipulation + +.namespace export +.if SYMON + acia_base = $8800 ; Symon +.else + acia_base = $dc00 +.endif + acia_data_reg = acia_base + acia_status_reg = acia_base + $1 + acia_cmd_reg = acia_base + $2 + acia_ctrl_reg = acia_base + $3 +.endn + +;;; init_acia +;;; Initialize acai for communication +;;; Input: +;;; - +;;; Output: +;;; - +;;; Changes: +;;; a, acai registers +.namespace export +init_acia: +.endn + .block + ;; Reset acai + sta export.acia_status_reg + lda #%00011111 ; 19200 bps, 8 data bits, 1 stop bit + sta export.acia_ctrl_reg + ;; No parity, no echo, no interrupts, DTR ready + lda #%11001011 + sta export.acia_cmd_reg + rts + .bend + +.namespace export +.if SYMON + via_start = $8000 +.else + via_start = $d800 +.endif + via_irb = via_start + via_orb = via_start + via_ira = via_start+$1 + via_ora = via_start+$1 + via_ddrb = via_start+$2 + via_ddra = via_start+$3 + via_t1cl = via_start+$4 + via_t1ch = via_start+$5 + via_t1ll = via_start+$6 + via_t1lh = via_start+$7 + via_t2cl = via_start+$8 + via_t2ch = via_start+$9 + via_sr = via_start+$10 + via_acr = via_start+$11 + via_pcr = via_start+$12 + via_ifr = via_start+$13 + via_ier = via_start+$14 + via_ira2 = via_start+$15 + via_ora2 = via_start+$15 +.endn + +;;; ---------------------------------------------------------- +;;; STANDARD LIBRARY FUNCTIONS +;;; +;;; These functions may be used by user programs. Labels +;;; marked with 'EXPORT' are exported to liba.h. When +;;; the user program imports this header file, the functions +;;; are available. +;;; ---------------------------------------------------------- + +;;; puts +;;; Send up to 256 characters terminated by null via acia +;;; Input: +;;; puts_str, put_str+1: +;;; 2 bytes on zero page containing string address +;;; Output: +;;; - +;;; Changes: +;;; a, x, y, puts_str, put_str+1 +.namespace export +puts: +.endn + .block + ;; Send string terminated by '\0' + ldy #$00 +_puts_loop: + lda (export.puts_str),y + beq _puts_end + phy + jsr export.putc + ply + iny + jmp _puts_loop +_puts_end: + rts + .bend + +;;; putc +;;; Send character via acia +;;; Input: +;;; a: +;;; Character to be printed +;;; Output: +;;; - +;;; Changes: +;;; x, acai registers +.namespace export +putc: +.endn + .block + ;; Send character. + ;; Due to bugs in the register and interrupt + ;; handling of the WDC 65C02, we have to use + ;; a manual delay. + sta export.acia_data_reg +.switch CLOCK_SPEED +.case 4 ; 4 Mhz Clock + SERIAL_SEND_DELAY_X = $c3 + SERIAL_SEND_DELAY_Y = $02 +.case 2 ; 4 Mhz Clock + SERIAL_SEND_DELAY_X = $c2 + SERIAL_SEND_DELAY_Y = $01 +.default ; 1 Mhz Clock + SERIAL_SEND_DELAY_X = $88 + SERIAL_SEND_DELAY_Y = $01 +.endswitch + ldy #SERIAL_SEND_DELAY_Y +outer_loop: + ldx #SERIAL_SEND_DELAY_X +inner_loop: + dex + bne inner_loop + dey + bne outer_loop + rts + .bend + +;;; puth +;;; Convert byte to hex and send via acia +;;; Input: +;;; a: +;;; Byte to be printed +;;; Output: +;;; - +;;; Changes: +;;; a, x, acai registers, c-flag +.namespace export +puth: +.endn + .block + ;; Send byte a as hex number + pha + lsr a + lsr a + lsr a + lsr a + jsr export.puth_nibble + pla + jsr export.puth_nibble + rts + .bend + +;;; puth_nibble +;;; Convert lower half of byte to hex and send via acia +;;; Input: +;;; a: +;;; Nibble to be printed +;;; Output: +;;; - +;;; Changes: +;;; a, x, acai registers, c-flag +.namespace export +puth_nibble: +.endn + .block + ;; Print hex digit + clc + and #$0F + adc #$30 ; Decimal number + cmp #$3A ; >10 ? + bmi _puth_putc + adc #$26 +_puth_putc: + jsr export.putc + rts + .bend + +;;; putd +;;; Output decimal number +;;; Input: +;;; a: +;;; The number +;;; Output: +;;; - +;;; Changes: +;;; a, x, y, acai registers, c-flag +.namespace export +putd: +.endn + .block + ldy #$00 +first_digit: + cmp #100 + bcc first_digit_set + sec + sbc #100 + iny + jmp first_digit +first_digit_set: + ldx #$00 + pha + tya + beq skip_first_leading_zero + jsr export.puth_nibble + ldx #$01 ; Force printing of zeros in rest of term +skip_first_leading_zero: + pla +second_digit: + cmp #10 + bcc second_digit_set + sec + sbc #10 + iny + jmp second_digit +second_digit_set: + pha + tya + cpx #$01 + beq do_not_skip_potential_second_leading_zero + cmp #$00 + beq skip_second_leading_zero +do_not_skip_potential_second_leading_zero: + jsr export.puth_nibble +skip_second_leading_zero: + pla + jmp export.puth_nibble + .bend + + +;;; putnl +;;; Send newline via acia +;;; Input: +;;; - +;;; Output: +;;; - +;;; Changes: +;;; a +.namespace export +putnl: +.endn + .block + lda #$0d + jsr export.putc + lda #$0a + jsr export.putc + rts + .bend + +;;; getc +;;; Read character from acia. Blocks until character is read +;;; Input: +;;; - +;;; Output: +;;; a: +;;; Character read +;;; Changes: +;;; a, acai registers +.namespace export +getc: +.endn + .block + ;; Read character from acia + lda export.acia_status_reg + and #%00001000 + beq export.getc + lda export.acia_data_reg + rts + .bend + +;;; getc_seed_rng +;;; Like getc, but also updates lsfr +;;; This is our only source of randomness right now: +;;; While we are busy waiting for a character, lsfr is +;;; updated. +;;; Input: +;;; - +;;; Output: +;;; a: +;;; Character read +;;; Changes: +;;; a, acai registers, lsfr_state +.namespace export +getc_seed_rng: +.endn + .block + ;; Read character from acia + ;; We also use the time between keystrokes + ;; as entropy source for the RNG + jsr export.lfsr_step + lda export.acia_status_reg + and #%00001000 + beq export.getc_seed_rng + lda export.acia_data_reg + rts + .bend + +.namespace export +getc_nonblocking: +.endn + .block + ;; Non-blocking read: If + ;; character has been read, + ;; it is returned in A and + ;; C is cleared. If not, + ;; C is set + sec + lda export.acia_status_reg + and #%00001000 + beq nothing_read + clc + lda export.acia_data_reg +nothing_read: + rts + .bend + +;;; gets +;;; Read up to 255 characters terminated by CR via acia +;;; Input: +;;; gets_len: +;;; Max. length of input. +;;; gets_str, gets_str+1: +;;; 2 bytes on zero page containing destination address +;;; +-------------------------------------------------------+ +;;; |IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! | +;;; +-------------------------------------------------------+ +;;; Note that a terminating zero is added to the string. +;;; Therefore gets_len+1 bytes may be written! +;;; Output: +;;; Zero terminated string at gets_str, gets_str+1 +;;; Changes: +;;; a, y, ACAI registers +.namespace export +gets: +.endn + .block + ;; Read string terminated by CR + ldy #$00 +loop: + phy + jsr export.getc + jsr export.putc + ply + cmp #$0d ; GOT CR? + beq terminate_string ; if not + sta (export.gets_str), y ; store character + iny ; if max length + cpy export.gets_len ; not reached, + bne loop ; continue loop. +terminate_string: + lda #$00 + sta (export.gets_str), y + rts + .bend + +;;; str2byte +;;; Convert number (<= 255) given as string to byte. +;;; Expects zero-terminated string on stack +;;; Input: +;;; Zero-terminated string on stack +;;; Output: +;;; a: +;;; Byte value +;;; Changes: +;;; a, x, y +.namespace export +str2byte: +.endn + .block + jsr export.create_stack_frame + PUSH $04 ; Local variable (string of length four) + lda #$00 + pha + ldy #$00 +add_digit: + lda (export.ds_frame),y + beq done + sec ; Convert next digit + sbc #'0' ; from ascii to + sta (export.ds_frame),y ; number. + pla ; Multiply current + jsr export.multiply_by_ten ; accumulator by 10. + clc ; Add new + adc (export.ds_frame),y ; digit. + pha + iny ; Advance to next digit + jmp add_digit +done: + pla + sta_LOCAL 0 + jsr export.delete_stack_frame + .bend + +;;; multiply_by_ten +;;; Multiply A by ten. +;;; Input: +;;; a: Number +;;; Output: +;;; a: Number * 10 +;;; Changes: +;;; a, c +.namespace export +multiply_by_ten: +.endn + .block + sta (export.ds_pointer) + lda #$00 + clc + .for i = 0, i < 10, i += 1 + adc (export.ds_pointer) + .next + rts + .bend diff --git a/roms/boot/io.inc b/roms/boot/io.inc @@ -0,0 +1,100 @@ +;;; -*- asm -*- + +;;; PRINT(addr) +;;; Send zero terminated string at addr via acia. +;;; Input: +;;; addr: +;;; Address of zero terminated string (<= 256 characters) +;;; Output: +;;; - +;;; Changes: +;;; a, x, y, puts_str, put_str+1 + +PRINT .macro + lda #<\1 + sta export.puts_str + lda #>\1 + sta export.puts_str+1 + jsr export.puts + .endm + +;;; PRINTS(string) (<= 255 characters) +;;; Send string via acia +;;; Input: +;;; string: +;;; String to be send. +;;; Output: +;;; - +;;; Changes: +;;; a, x, y, puts_str, put_str+1 + +PRINTS .macro + PRINT(saddr) + jmp cont_PRINTS +saddr: + .null \1 +cont_PRINTS: + .endm + +;;; PRINTSNL(string) (<= 253 characters) +;;; Send string and newline via acia +;;; Input: +;;; string: +;;; String to be send. +;;; Output: +;;; - +;;; Changes: +;;; a, x, y, puts_str, put_str+1 + +PRINTSNL .macro + PRINT(saddr) + jmp cont_PRINTSNL +saddr: + .null \1, $0d, $0a +cont_PRINTSNL: + .endm + +;;; INPUTS(addr, len) +;;; Read up to len characters from acai and store them at addr +;;; This is a wrapper for gets +;;; Input: +;;; addr: +;;; Target address for zero-terminated string +;;; len: +;;; Maximal length of string to be read (not terminating zero!) +;;; +-------------------------------------------------------+ +;;; |IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! | +;;; +-------------------------------------------------------+ +;;; Note that a terminating zero is added to the string. +;;; Therefore len+1 bytes may be written! +;;; Output: +;;; - +;;; Changes: +;;; a, y, ACAI registers + +INPUTS .macro + lda #<\1 + sta export.gets_str + lda #>\1 + sta export.gets_str+1 + lda \2 + sta export.gets_len + jsr export.gets + .endm + +;;; PRINTNL +;;; Send newline via acia +;;; Input: +;;; - +;;; Output: +;;; - +;;; Changes: +;;; a, x, acai registers + +PRINTNL .macro + lda #$0d + jsr export.putc + lda #$0a + jsr export.putc + .endm + diff --git a/roms/boot/lfsr.asm b/roms/boot/lfsr.asm @@ -0,0 +1,61 @@ +;;; 16 Bit Galouis LFSR +;;; If you use the whole state as RNG, the quality of the +;;; random numbers is rather poor. Just extracting one +;;; bit from the state yields random numbers with reasonable +;;; statistical properties. + +;;; lfsr_init +;;; Initialize lfsr +;;; Input: +;;; - +;;; Output: +;;; - +;;; Changes: +;;; lfsr +.namespace export +lfsr_init: +.endn + .block + #STORE_WORD $beef, export.lfsr_state + .bend + +;;; lfsr_step +;;; update lfsr +;;; Input: +;;; - +;;; Output: +;;; - +;;; Changes: +;;; lfsr +.namespace export +lfsr_step: +.endn + .block + asl export.lfsr_state + lda export.lfsr_state+1 + rol a + bcc cont + eor #$0b +cont: + sta export.lfsr_state+1 + lda export.lfsr_state + adc #$00 + sta export.lfsr_state + rts + .bend + +.if false +;;; Test function for LFSR +test_lfsr: .block +loop: + jsr export.lfsr_step + lda export.lfsr_state+1 + jsr puth + lda export.lfsr_state + jsr export.puth + PRINTSNL("") + jsr export.getc + jmp loop + .bend +.endif ; false + diff --git a/roms/boot/sd_card.asm b/roms/boot/sd_card.asm @@ -0,0 +1,266 @@ +;;; ----------------------------------- +;;; +;;; SD card communication interface +;;; +;;; ----------------------------------- + + +;;; Send command +.namespace export +sd_send_cmd: +.endn + .block + ldy #$00 +cmd_bytes_loop: + lda (export.sd_data),y + jsr export.spi_set + iny + cpy #$06 + bne cmd_bytes_loop + lda #$ff ; Finalize transmission + jsr export.spi_set ; with dummy byte + rts + .bend + + +.namespace export +print_block: +.endn + .block + ldx #$02 + ldy #$00 +loop: + phx + phy + lda (export.sd_data),y + jsr export.puth + PRINTS " " + ply + plx + iny + bne loop + ADD_WORD export.sd_data, $0100 + dex + bne loop + PRINTNL + SUB_WORD export.sd_data, $0200 + rts + .bend + +.namespace export +sd_read_block: +.endn + ;; Read a block of data + ;; Block number is passed via stack. + .block + jsr export.create_stack_frame + PUSH $04 ; Local variable (32 bit block number) +.if DEBUG + PRINTSNL "Sending CMD17" +.endif + lda #$51 ; CMD17 (READ_SINGLE_BLOCK) + jsr export.spi_set + jsr sd_send_block_number + lda #$01 ; Dummy CRC + jsr export.spi_set + jsr export.spi_get + cmp #$00 + beq wait_for_start_token + jmp read_failed +wait_for_start_token: + jsr export.spi_get_nonblocking + cmp #$ff + beq wait_for_start_token + cmp #$fe + bne read_failed + ldx #$02 + ldy #$00 +loop: + phx + phy + jsr export.spi_get_nonblocking + ply + plx + sta (export.sd_data),y + iny + bne loop + ADD_WORD export.sd_data, $0100 ; Second half of block + dex + bne loop + SUB_WORD export.sd_data, $0200 ; Restore pointer + clc + SD_RECEIVE $2 ; Get CRC + jsr export.delete_stack_frame + rts +read_failed: + PRINTSNL("Error reading SD card!") + sec + jsr export.delete_stack_frame + rts + .bend + +sd_send_block_number: + .block + lda export.sd_type ; Old cards expect the position + cmp #$05 ; as bytes, not blocks + beq old_card + lda_LOCAL 0 ; Block number + jsr export.spi_set + lda_LOCAL 1 + jsr export.spi_set + lda_LOCAL 2 + jsr export.spi_set + lda_LOCAL 3 + jsr export.spi_set + rts +old_card: + lda_LOCAL 3 + asl a + sta_LOCAL 3 + lda_LOCAL 2 + rol a + sta_LOCAL 2 + lda_LOCAL 1 + rol a + jsr export.spi_set + lda_LOCAL 2 + jsr export.spi_set + lda_LOCAL 3 + jsr export.spi_set + lda #$00 + jsr export.spi_set + rts + .bend + +.namespace export +sd_write_block: +.endn + ;; Write block at sd_data to card. + ;; Block number is passed via stack. + .block + jsr export.create_stack_frame + PUSH $04 ; Local variable (32 bit block number) +.if DEBUG + PRINTSNL "Sending CMD24" +.endif + lda #$58 ; CMD24 (WRITE_BLOCK) + jsr export.spi_set + jsr sd_send_block_number + lda #$01 ; Dummy CRC + jsr export.spi_set + lda #$ff + jsr export.spi_set ; Dummy byte + SD_RECEIVE $1 + ;; Send start token + lda #$fe + jsr export.spi_set + ;; Send data + ldx #$02 + ldy #$00 +write_loop: + phx + phy + lda (export.sd_data),y + jsr export.spi_set + ply + plx + iny + cpy #$00 + bne write_loop + ADD_WORD export.sd_data, $0100 ; Second half of block + dex + cpx #$00 + bne write_loop + SUB_WORD export.sd_data, $0200 ; Restore pointer + ;; Wait for write operation to complete + SD_RECEIVE $1 + cmp #$e5 + bne error_writing +wait_loop: + SD_RECEIVE $1 + beq wait_loop + jsr export.delete_stack_frame + clc + rts +error_writing: + PRINTSNL("Error writing to SD card!") + jsr export.delete_stack_frame + sec + rts + .bend + +sd_close: + #SET_BITS export.via_ora, #CS + rts + +.namespace export +sd_open: +.endn + .block +.if DEBUG + PRINTSNL "Initializing SD Card" +.endif + ;; Configure ports for in- and output + SET_BITS export.via_ddra, #(CS | SCK | MOSI) + CLEAR_BITS export.via_ddra, #MISO + ldy #$10 ; Error counter + phy +start_init: + ;; Card power on sequence: + ;; Keep CS high, toggle clock for at least 74 cycles. + SET_BITS export.via_ora, #(CS | MOSI) + ;; Wait for 80 clock pulses + ldx #160 +boot_wait_loop: + TOGGLE_BITS export.via_ora, #SCK + dex + bne boot_wait_loop + CLEAR_BITS export.via_ora, #(CS|MOSI|SCK) + SD_SEND_CMD [$40, $00, $00, $00, $00, $95] ; CMD0 + SD_RECEIVE $1 + cmp #$01 ; Should be $01 (In idle state). + beq cont + ply + dey + phy + beq failed_init + jmp start_init ; If not, start over. +failed_init: + ply + PRINTSNL("Error initializing SD card!") + sec + rts +cont: + ;; Card is in SPI mode + ply + SD_SEND_CMD [$48, $00, $00, $01, $aa, $87] ; CMD8 + SD_RECEIVE $1 + SD_SEND_CMD [$45, $00, $00, $00, $00, $5b] ; CMD5 + SD_RECEIVE $1 ; $05 = SD; $c1 = SDHC + sta export.sd_type + SD_SEND_CMD [$7b, $00, $00, $00, $00, $83] ; CMD59 + SD_RECEIVE $1 ; should be $01 + ;; Send ACMD41 until card is initialized + ldy #$10 ; Fail counter + phy +wait_for_card_initialized: + SD_SEND_CMD [$77, $00, $00, $00, $00, $65] ; CMD55 + SD_RECEIVE $1 ; should be $01 + SD_SEND_CMD [$69, $40, $00, $00, $00, $95] ; ACMD41 (v1 & v2 card) + SD_RECEIVE $1 ; $01 if busy, $00 if ready (initialized) + cmp #$00 + beq initialized + ply + dey + phy + bne cont2 ; More tries? +cont2: + jmp wait_for_card_initialized +initialized: + ply + SD_SEND_CMD [$50, $00, $00, $02, $00, $15] ; CMD16 + SD_RECEIVE $1 + clc + rts + .bend + diff --git a/roms/boot/sd_card.inc b/roms/boot/sd_card.inc @@ -0,0 +1,48 @@ +;;; -*- asm -*- + +;;; Macros for accessing SD cards + +SD_SEND_CMD .macro +.if DEBUG + PRINTS "Sending CMD$" + lda cmd + sec + sbc #$40 + jsr export.puth + PRINTNL +.endif + lda #<cmd + sta export.sd_data + lda #>cmd + sta export.sd_data+1 + jsr export.sd_send_cmd + jmp cont_SD_SEND_CMD +cmd: .byte \1 +cont_SD_SEND_CMD: + .endm + + + +SD_RECEIVE .macro + ldx #\1 +loop_SD_RECEIVE: + phx + jsr export.spi_get +.if DEBUG + pha + jsr export.puth + PRINTS " " + pla +.endif + plx + dex + bne loop_SD_RECEIVE + pha +.if DEBUG + PRINTNL +.endif + ;; Read non-existing dummy byte for snychronization + jsr export.spi_get_nonblocking + pla + .endm + diff --git a/roms/boot/spi.asm b/roms/boot/spi.asm @@ -0,0 +1,102 @@ +;;; ----------------------------------- +;;; +;;; SPI read and write operations +;;; +;;; ----------------------------------- + + ;; Wiring: + ;; PA0: CS + CS = %00000001 + ;; PA1: SCK + SCK = %00000010 + ;; PA2: MOSI + MOSI = %00000100 + ;; PA3: MISO + MISO = %00001000 + + +;;; Initialize VIA: Switch all ports to output +;;; and all pins to low +.namespace export +init_via: + lda #$ff + sta export.via_ddra + sta export.via_ddrb + lda #$00 + sta export.via_ora + sta export.via_orb + rts +.endn + +;;; Blocking read - read until +;;; byte != $ff is read or retry counter +;;; expires. +.namespace export +spi_get: +.endn + .block + ldy #$00 ; Try up to $ff times +retry_loop: + jsr export.spi_get_nonblocking + ;; If we read $ff, we did not receive + ;; a byte. + cmp #$ff + bne done + dey ; Try again unless retry + bne retry_loop ; counter expired. +done: + CLEAR_BITS export.via_ora, #(MOSI|SCK) ; Set MOSI and SCK low on completion. + lda export.via_buffer + rts + .bend + +;;; Return next byte; may be $ff in case of no transmission +.namespace export +spi_get_nonblocking: +.endn + SET_BITS export.via_ora, #MOSI ; Keep MOSI high during read + lda #$00 ; Clear + sta export.via_buffer ; receive buffer. + ldx #$08 ; 8 bit to read. +read_bits_loop: + .block + asl export.via_buffer ; Make room for next bit + CLEAR_BITS export.via_ora, #SCK ; Generate clock impulse + SET_BITS export.via_ora, #SCK + lda export.via_ira ; Get next bit. + and #MISO + cmp #MISO + bne cont ; Nothing to do if we got a zero bit. + inc export.via_buffer ; Set lowest bit in via_buffer +cont: + dex + bne read_bits_loop + CLEAR_BITS export.via_ora, #(MOSI|SCK) ; Set MOSI and SCK low on completion. + lda export.via_buffer + rts + .bend + +;;; Write byte via SPI +.namespace export +spi_set: +.endn + .block + sta export.via_buffer ; We send from via_buffer + ldx #$08 ; 8 bit to write +write_bits_loop: + CLEAR_BITS export.via_ora, #SCK ; Set bits on low clock + CLEAR_BITS export.via_ora,#MOSI + lda export.via_buffer ; Get next bit to send + and #%10000000 ; Isolate bit to send. + beq cont ; Set MOSI accordingly. + SET_BITS export.via_ora,#MOSI + jmp cont +cont: + SET_BITS export.via_ora, #SCK ; Send bits on high clock + asl export.via_buffer ; Advance to next bit + dex + bne write_bits_loop + CLEAR_BITS export.via_ora,#SCK + rts + .bend + diff --git a/roms/boot/stack.asm b/roms/boot/stack.asm @@ -0,0 +1,72 @@ +;;; ---------------------------------------------------------- +;;; +;;; STACK +;;; +;;; ---------------------------------------------------------- + +;;; Software implementation of data stack. In difference to typical +;;; stack implementations, this stack grows from bottom to top, because +;;; we want to access eleements using indirected indexed access with +;;; the stack frame pointer as base. Therefore the stack should +;;; be initialized with the first unoccupied RAM address after the +;;; program code. +;;; +;;; PULL and PUSH do not move actual stack data, they just shift +;;; the stack pointer. +;;; +;;; Local variables on the data stack are supported by macro +;;; VAR. VAR i refers to the ith byte on the stack. PARAM allows +;;; to pass variables to a callee. PARAM i of the caller becomes +;;; VAR i of the callee. The callee should reserve stack space +;;; for both its parameters and local variables. +;;; +;;; Stack frames used for these calls are created/destroyed by +;;; subroutines create_stack_frame and delete_stack_frame. +;;; +;;; CALL is like jsr with parameter passing. There addressing +;;; modes are supported: immediate, zero-page, and local variables. +;;; For immediate and zero page, the third parameter must be lda. +;;; For local variables, the third parameter must be lda_LOCAL. +;;; See sw/stack_test/stack_test.asm for examles. + + +.namespace export +create_stack_frame: +.endn + .block + ;; Store old frame pointer on stack. + ;; Since our stack grows bottom to top, we write + ;; the data before increasing the stack pointer + #COPY_WORD_ABSOLUTE_INDIRECT export.ds_frame, (export.ds_pointer) + #PUSH $02 ; Store old frame pointer + #COPY_WORD_ABSOLUTE_INDIRECT export.ds_pointer, export.ds_frame ; Set new frame pointer + rts + .bend + +.namespace export +delete_stack_frame: +.endn + .block + #COPY_WORD_ABSOLUTE_INDIRECT export.ds_frame, export.ds_pointer ; Reset stack pointer to begin of frame. + #PULL $02 ; Move stack pointer to frame reference + #COPY_WORD_ABSOLUTE_INDIRECT (export.ds_pointer), export.ds_frame ; Restore old frame pointer. + rts + .bend + +.namespace export +push_a: +.endn + .block + sta (export.ds_pointer) + #PUSH $01 + rts + .bend + +.namespace export +pull_a: +.endn + .block + #PULL $01 + lda (export.ds_pointer) + rts + .bend diff --git a/roms/boot/stack.inc b/roms/boot/stack.inc @@ -0,0 +1,77 @@ +;;; -*- asm -*- + +;;; Macros for accessing the data stack +;;; and managing local variables and +;;; call parameters. Check stack.asm for +;;; documentation. + +;;; Stack related macros + +INIT_STACK: .macro + #STORE_WORD \1, export.ds_pointer + .endm + +PUSH: .macro ; Reserve bytes on stack (no actual push operation) + #ADD_WORD export.ds_pointer, \1 + .endm + +PULL: .macro ; Remove bytes from stack (no return value) + #SUB_WORD export.ds_pointer, \1 + .endm + +PUSH_WORD: .macro ; Push a word on the stack + lda #<\1 + sta (export.ds_pointer) + lda #>\1 + ldy #$01 + sta (export.ds_pointer),y + #PUSH $02 + .endm + +LOCAL: .macro + ldy #\1 + \2 (export.ds_frame),y + .endm +lda_LOCAL: .macro + LOCAL \1, lda + .endm +sta_LOCAL: .macro + LOCAL \1, sta + .endm +adc_LOCAL: .macro + LOCAL \1, adc + .endm +PARAM: .macro + ldy #\1 + \2 (export.ds_pointer),y + .endm +sta_PARAM: .macro + PARAM \1+2, sta + .endm +lda_PARAM: .macro + PARAM \1+2, lda + .endm + +CALL: .macro subroutine, param=[], param_lda="" + .block + .if len(\param) != 0 + .for CALL_param := 0, CALL_param < len(\param), CALL_param += $1 + \param_lda \param[CALL_param] + sta_PARAM CALL_param + .next + .endif + jsr \subroutine + .bend + .endm + +;; Push sequence of bytes to stack. Parameters: From, Length +PUSH_RANGE: .macro + ldy #$00 +loop_PUSH_RANGE: + lda \1,y + sta (ds_pointer),y + iny + cpy \2 + bne loop_PUSH_RANGE + #PUSH \2 + .endm diff --git a/sw/10print/10print.asm b/sw/10print/10print.asm @@ -11,11 +11,12 @@ .else .if SYMON .include "boot_symon.l" + * = $0300 .else .include "boot.l" + * = $0200 .endif - .include "boot_macros.inc" - * = $0300 + .include "boot.inc" .endif init: cld diff --git a/sw/SW.txt b/sw/SW.txt @@ -1,3 +1,14 @@ +* load_from_card/ + +Reads directory and prints it on serial line. The user can choose a +program to run. This is the default program included by +roms/boot/boot.bin. Refer to this program as an example on how to +access the SD card and how to use the data stack. + +* ttt/ + +Tic-Tac-Toe + * mem_test/ Tests RAM write and read operations. Used to test if the CPU interacts @@ -15,10 +26,19 @@ https://10print.org/ Output stream of 'a'. Used to calliberate serial interface. -* interrupts +* interrupts/ Testing interrupt handling. -* ttt/ -Tic-Tac-Toe +* stack_test/ + +Testing the data stack. + +* via_test/ + +Testing VIA GPIO. + +* ansi_term/ + +Testing ANSI term commands. diff --git a/sw/ansi_test/Makefile b/sw/ansi_test/Makefile @@ -0,0 +1,3 @@ +TARGET=ansi_test + +include ../Makefile.common diff --git a/sw/ansi_test/ansi_test.asm b/sw/ansi_test/ansi_test.asm @@ -0,0 +1,143 @@ +.if SYMON + .include "boot_symon.l" + start_address = $0300 +.else + .include "boot.l" + start_address = $0200 +.endif + .include "boot.inc" + * = rom_zero_page_end + .dsection zero_page + * = start_address + .dsection code + .cerror * > $8000, "RAM exhausted" + +.section code + start_of_stack = end_of_code ; Stack starts right after code +init: + cld + jsr init_acia + INIT_STACK start_of_stack + ;; Termin program on PC needs some time to start up + ldy #$80 + ldx #$ff +delay: + dex + bne delay + dey + bne delay + jsr ansi_test + jsr getc + jsr print_all_colors +.if false +.endif +end: + jmp end + +ansi_test: + .block + jsr create_stack_frame + jsr clear_screen + #SET_CURSOR #$01, #$01 + #PRINTSNL "ANSI Terminal" + #SET_CURSOR #$03, #$02 + PRINTS "Text starting at position 3, 2" + #SET_CURSOR #$02, #$03 + PRINTSNL "Text starting at position 2, 3" + PRINTS "Cursor position: " + jsr get_cursor + pha + txa + jsr putd + PRINTS ", " + pla + jsr putd + #SET_CURSOR #$01, #$05 + PRINTS "Screen size: " + jsr get_screen_size + pha + txa + jsr putd + PRINTS ", " + pla + jsr putd + PRINTNL + #SET_TEXT bold + PRINTSNL "Bold text" + #SET_TEXT plain + #SET_TEXT blink + PRINTSNL "Blinking text" + #SET_TEXT plain + #SET_TEXT reverse + PRINTSNL "Reverse text" + #SET_TEXT plain + lda #"*" + jsr putc + #CURSOR_DOWN 1 + #CURSOR_BACK 1 + lda #"*" + jsr putc + #CURSOR_DOWN 1 + #CURSOR_BACK 1 + lda #"*" + jsr putc + #CURSOR_FORWARD 1 + lda #"*" + jsr putc + #CURSOR_BACK 1 + #CURSOR_UP 1 + lda #"*" + jsr putc + #CURSOR_BACK 1 + #CURSOR_UP 1 + lda #"*" + jsr putc + lda #$03 + jsr cursor_down + lda #$03 + jsr cursor_back + PRINTSNL "ANSI test completed." + rts + .bend + +print_all_colors: + .block + jsr clear_screen + .for color in black, red, green, yellow, blue, magenta, cyan, white + #SET_COLOR color + PRINTSNL "Foreground color" + .next + #SET_TEXT plain + .for color in black, red, green, yellow, blue, magenta, cyan, white + #SET_BACKGROUND_COLOR color + PRINTS "Background color" + #SET_TEXT plain + PRINTNL + .next + .for color in (bright_black, bright_red, bright_green, bright_yellow, + bright_blue, bright_magenta, bright_cyan, bright_white) + #SET_COLOR color + PRINTSNL "Foreground color" + .next + lda #$00 +loop: + pha + jsr set_color + pla + pha + jsr putd + lda #" " + jsr putc + pla + pha + inc a + cmp #$00 + bne loop + PRINTNL + jsr delete_stack_frame + #SET_TEXT plain + rts + .bend + +end_of_code: +.send code diff --git a/sw/load_from_card/Makefile b/sw/load_from_card/Makefile @@ -0,0 +1,3 @@ +TARGET=load_from_card + +include ../Makefile.common diff --git a/sw/load_from_card/load_from_card.asm b/sw/load_from_card/load_from_card.asm @@ -0,0 +1,194 @@ +;;; Start program form SD card. +;;; If we are included in boot.asm, we +;;; should not set the start address +.weak + BOOT_EMBEDDED = false +.endweak +.if BOOT_EMBEDDED + init_acia = export.init_acia + init_via = export.init_via + getc = export.getc + putc = export.putc + create_stack_frame = export.create_stack_frame + ds_pointer = export.ds_pointer + sd_data = export.sd_data + sd_read_block = export.sd_read_block + sd_open = export.sd_open + puts_str = export.puts_str + puts = export.puts + puth = export.puth +.else + .if SYMON + .include "boot_symon.l" + start_address = $0300 + .else + .include "boot.l" + start_address = $0200 + .endif + .include "boot.inc" + * = rom_zero_page_end + .dsection zero_page + * = start_address +.endif +.dsection code + +.section code + +;;; ----------------------------------- +;;; +;;; Main +;;; +;;; ----------------------------------- + +init: + .block +.if SYMON + cld + jsr init_acia + jsr init_via +.endif +.if !BOOT_EMBEDDED + ;; Wait for key to start + jsr getc +.endif + INIT_STACK $7fff-$0400 +loop: + jsr ls + jsr choose_program + bcs loop + jsr execute + jmp init + .bend + + load_address = $0200 + +choose_program: + PRINTSNL("Choose program (0-9). Any other key reloads card.") + jsr getc + sec + sbc #'0' + cmp #$0a ; Sets carry if not a digit + rts + + +execute: + .block + pha + jsr create_stack_frame + PUSH 4 ; Local variables + pla + ;; Each app has 2**16 blocks, app number is in A, therefore + ;; the address of the app is [#$00, A, #$00, #$01] + sta_LOCAL 1 + PRINTS "Executing program " + lda_LOCAL 1 + clc + adc #'0' + jsr putc + PRINTSNL "." + lda #$00 + sta_LOCAL 0 + sta_LOCAL 2 + lda #$01 + sta_LOCAL 3 + COPY_WORD_ABSOLUTE_INDIRECT ds_pointer, sd_data ; Read block + PUSH $0200 ; to stack + CALL sd_read_block, [0, 1, 2, 3], lda_LOCAL + PULL $0200 ; Reset stack pointer to block read + COPY_WORD_IMMEDIATE load_address, sd_data ; Set Target address + lda (ds_pointer) ; Number of blocks +read_next_block: + pha + ;; Advance to next block on card + clc + lda_LOCAL 3 + adc #$01 + sta_LOCAL 3 + lda_LOCAL 2 + adc #$00 + sta_LOCAL 2 + CALL sd_read_block, [0, 1, 2, 3], lda_LOCAL ; Read block + ADD_WORD sd_data, $0200 ; Advance to next block + pla + dec a + bne read_next_block + ;; Done. Execute program + jmp load_address + .bend + + + ;; List programs on card +ls: + .block + jsr create_stack_frame + jsr sd_open + ;; List files on card + COPY_WORD_ABSOLUTE_INDIRECT ds_pointer, sd_data ; Read block + PUSH $0200 ; to stack + CALL sd_read_block, [#$00, #$00, #$00, #$00], lda + PULL $0200 ; Reset stack pointer to point to block read + lda (ds_pointer) + cmp #$00 ; Check FS version + beq cont + jmp not_supported +cont: + lda #$00 ; File number +dir_entry_loop: + pha + clc + adc #'0' + jsr putc + PRINTS ": " + ;; Each app has 2**16 blocks, therefore + ;; the address of app i is [#$00, #i, #$00, #$01] + lda #$00 + sta_LOCAL 0 + sta_LOCAL 2 + lda #$01 + sta_LOCAL 3 + pla + pha + sta_LOCAL 1 + PUSH 4 + COPY_WORD_ABSOLUTE_INDIRECT ds_pointer, sd_data ; Read block + PUSH $0200 ; to stack + CALL sd_read_block, [0, 1, 2, 3], lda_LOCAL + PULL $0200 ; Reset stack pointer to block read + lda (ds_pointer) ; Number of blocks + beq filename_printed ; This spot is not occupied + pha + COPY_WORD_ABSOLUTE_INDIRECT ds_pointer, puts_str + ADD_WORD puts_str, $01 ; Start of filename + jsr puts + PRINTS " ($" + pla + pha + jsr puth + pla + cmp #$01 + beq one_block + PRINTS " blocks)" + jmp filename_printed +one_block: + PRINTS " block)" +filename_printed: + PRINTNL + PULL 4 + pla + inc a + cmp #$0a + bcs done + jmp dir_entry_loop +done: + clc + jsr export.delete_stack_frame + rts +not_supported: + PRINTSNL "This filesystem is not supported." + sec + jsr export.delete_stack_frame + rts + .bend + +end_of_code: +.send code diff --git a/sw/sd_card/Makefile b/sw/sd_card/Makefile @@ -1,3 +0,0 @@ -TARGET=sd_card - -include ../Makefile.common diff --git a/sw/sd_card/sd_card.asm b/sw/sd_card/sd_card.asm @@ -1,182 +0,0 @@ -;;; Start program form SD card. -;;; If we are included in boot.asm, we -;;; should not set the start address -.weak - BOOT_EMBEDDED = false -.endweak -.if BOOT_EMBEDDED - init_acia = export.init_acia - init_via = export.init_via - getc = export.getc - putc = export.putc - new_stack_frame = export.new_stack_frame - ds_pointer = export.ds_pointer - sd_data = export.sd_data - sd_read_block = export.sd_read_block - sd_open = export.sd_open - puts_str = export.puts_str - puts = export.puts - puth = export.puth -.else - .if SYMON - .include "boot_symon.l" - .else - .include "boot.l" - .endif - .include "boot_macros.inc" - * = rom_zero_page_end - .dsection zero_page - * = $0300 -.endif -.dsection code - -.section code - -;;; ----------------------------------- -;;; -;;; Main -;;; -;;; ----------------------------------- - -init: - .block -.if SYMON - cld - jsr init_acia - jsr init_via -.endif -.if !BOOT_EMBEDDED - ;; Wait for key to start - jsr getc -.endif - INIT_STACK $7fff-$0400 -loop: - CALL ls, [], lda - jsr choose_program - bcs loop - jsr execute - jmp init - .bend - - load_address = $0300 - -choose_program: - PRINTSNL("Choose program (0-9). Any other key reloads card.") - jsr getc - sec - sbc #'0' - cmp #$0a ; Sets carry if not a digit - rts - - -execute: - .block - pha - jsr new_stack_frame - pla - ;; Each app has 2**16 blocks, app number is in A, therefore - ;; the address of the app is [#$00, A, #$00, #$01] - sta_LOCAL 1 - PRINTS "Executing program " - lda_LOCAL 1 - clc - adc #'0' - jsr putc - PRINTSNL "." - lda #$00 - sta_LOCAL 0 - sta_LOCAL 2 - lda #$01 - sta_LOCAL 3 - PUSH 4 - COPY_WORD_ABSOLUTE_INDIRECT ds_pointer, sd_data ; Read block - PUSH $0200 ; to stack - CALL sd_read_block, [0, 1, 2, 3], lda_LOCAL - PULL $0200 ; Reset stack pointer to block read - COPY_WORD_IMMEDIATE load_address, sd_data ; Set Target address - lda (ds_pointer) ; Number of blocks -read_next_block: - pha - ;; Advance to next block on card - clc - lda_LOCAL 3 - adc #$01 - sta_LOCAL 3 - lda_LOCAL 2 - adc #$00 - sta_LOCAL 2 - CALL sd_read_block, [0, 1, 2, 3], lda_LOCAL ; Read block - ADD_WORD sd_data, $0200 ; Advance to next block - pla - dec a - bne read_next_block - ;; Done. Execute program - jmp load_address - .bend - - - ;; List programs on card -ls: - .block - jsr sd_open - ;; List files on card - COPY_WORD_ABSOLUTE_INDIRECT ds_pointer, sd_data ; Read block - PUSH $0200 ; to stack - CALL sd_read_block, [#$00, #$00, #$00, #$00], lda - PULL $0200 ; Reset stack pointer to point to block read - lda (ds_pointer) - cmp #$00 ; Check FS version - beq cont - jmp not_supported -cont: - lda #$00 ; File number -dir_entry_loop: - pha - clc - adc #'0' - jsr putc - PRINTS ": " - ;; Each app has 2**16 blocks, therefore - ;; the address of app i is [#$00, #i, #$00, #$01] - lda #$00 - sta_LOCAL 0 - sta_LOCAL 2 - lda #$01 - sta_LOCAL 3 - pla - pha - sta_LOCAL 1 - PUSH 4 - COPY_WORD_ABSOLUTE_INDIRECT ds_pointer, sd_data ; Read block - PUSH $0200 ; to stack - CALL sd_read_block, [0, 1, 2, 3], lda_LOCAL - PULL $0200 ; Reset stack pointer to block read - lda (ds_pointer) ; Number of blocks - beq filename_printed ; This spot is not occupied - pha - COPY_WORD_ABSOLUTE_INDIRECT ds_pointer, puts_str - ADD_WORD puts_str, $01 ; Start of filename - jsr puts - PRINTS " ($" - pla - jsr puth - PRINTS " blocks)" -filename_printed: - PRINTNL - PULL 4 - pla - inc a - cmp #$0a - bcs done - jmp dir_entry_loop -done: - clc - RETURN -not_supported: - PRINTSNL "This filesystem is not supported." - sec - RETURN - .bend - -end_of_code: -.send code diff --git a/sw/serial_line_echo/serial_line_echo.asm b/sw/serial_line_echo/serial_line_echo.asm @@ -3,8 +3,8 @@ .else .include "boot.l" .endif - .include "boot_macros.inc" - * = $0300 + .include "boot.inc" + * = $0200 init: jsr init_acia diff --git a/sw/stack_test/stack_test.asm b/sw/stack_test/stack_test.asm @@ -1,12 +1,14 @@ ;;; A data stack .if SYMON .include "boot_symon.l" + start_address = $0300 .else .include "boot.l" + start_address = $0200 .endif - .include "boot_macros.inc" + .include "boot.inc" - * = $300 + * = start_address .dsection code .cerror * > $8000, "RAM exhausted" @@ -25,7 +27,7 @@ init: .endif jsr stack_test jsr test_local_variables - CALL test_recursion, [], lda_LOCAL + jsr test_recursion .if false .endif loop: @@ -35,8 +37,8 @@ loop: stack_test: .block ;; Testing empty stack - jsr new_stack_frame - jsr remove_stack_frame + jsr create_stack_frame + jsr delete_stack_frame lda ds_pointer cmp #<start_of_stack #CHECK_ERROR @@ -44,7 +46,7 @@ stack_test: cmp #>start_of_stack #CHECK_ERROR ;; Push and pull - jsr new_stack_frame + jsr create_stack_frame lda #$a0 sta (ds_pointer) PUSH $01 @@ -73,7 +75,7 @@ stack_test: PUSH $01 lda ds_pointer ; Save ds_pointer pha ; for later comparison - jsr new_stack_frame + jsr create_stack_frame ldy #$00 lda #$b1 sta (ds_pointer),y @@ -87,7 +89,7 @@ stack_test: lda (ds_pointer) cmp #$b1 #CHECK_ERROR - jsr remove_stack_frame + jsr delete_stack_frame pla ; Still same ds_pointer cmp ds_pointer ; as before creating and #CHECK_ERROR ; deleting frame? @@ -99,7 +101,7 @@ stack_test: lda (ds_pointer) cmp #$a0 #CHECK_ERROR - jsr remove_stack_frame + jsr delete_stack_frame lda ds_pointer ; Stack should be cmp #<start_of_stack ; empty again. #CHECK_ERROR @@ -130,7 +132,7 @@ error_cont: test_local_variables: .block - jsr new_stack_frame + jsr create_stack_frame ;; Working with local variables PUSH $02 ; Create local variables lda #$ab @@ -152,25 +154,26 @@ test_local_variables: lda_PARAM 0 cmp #$03 #CHECK_ERROR - jsr remove_stack_frame + jsr delete_stack_frame PRINTSNL("Local variable test completed!") rts .bend test_param: .block - jsr new_stack_frame + jsr create_stack_frame PUSH $02 ; Local variables (here: parameters) lda_LOCAL 0 clc adc_LOCAL 1 sta_LOCAL 0 - jsr remove_stack_frame + jsr delete_stack_frame rts .bend test_recursion: .block + jsr create_stack_frame PUSH $02 ; Create local variables lda #$04 ; Recursion counter sta_LOCAL 0 @@ -180,7 +183,7 @@ test_recursion: lda_PARAM 1 ; Check for cmp #$09 ; expected result #CHECK_ERROR - jsr remove_stack_frame + jsr delete_stack_frame ;; Check if stack is empty again lda ds_pointer cmp #<start_of_stack @@ -194,7 +197,8 @@ test_recursion: recursion: .block - PUSH $02 ; Local variables (parameters) + jsr create_stack_frame + PUSH $02 ; Local variables lda_LOCAL 0 beq done dec a @@ -207,7 +211,8 @@ recursion: lda_PARAM 1 sta_LOCAL 1 done: - RETURN + jsr delete_stack_frame + rts .bend diff --git a/sw/ttt/ttt.asm b/sw/ttt/ttt.asm @@ -14,13 +14,15 @@ .else .if SYMON .include "boot_symon.l" + start_address = $0300 .else .include "boot.l" + start_address = $0200 .endif - .include "boot_macros.inc" + .include "boot.inc" * = rom_zero_page_end .dsection zero_page - * = $0300 + * = start_address .endif .dsection ttt_game diff --git a/sw/via_test/via_test.asm b/sw/via_test/via_test.asm @@ -4,9 +4,9 @@ .else .include "boot.l" .endif - .include "boot_macros.inc" + .include "boot.inc" - * = $300 + * = $200 .dsection code .cerror * > $8000, "RAM exhausted" diff --git a/tools/boot.py b/tools/boot.py @@ -35,7 +35,6 @@ def upload_program(filename): print("Uploading program.") e = str.encode(chr(blocks)) s.write(e) - # While transferring data, we calculate a two byte # checksum. The first byte is the addition of all bytes diff --git a/tools/reset.py b/tools/reset.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +"""Trigger reset on 8-bit computer""" + +import serial +import os + +SERIAL_PORT = '/dev/ttyUSB0' +SERIAL_SPEED = 19200 + +with serial.Serial(SERIAL_PORT, SERIAL_SPEED) as s: + print("Triggering reset.") + os.system('stty -F ' + SERIAL_PORT + ' -hup') + l = "" + while l != b'READY!\r\n': + s.setDTR(False) + s.setDTR(True) + l = s.readline()