68K CPU with Frame Buffer on FPGA

Working with AMR's build and improvements to the TG68 FPGA project

Public Chat
Similar projects worth following
Starting from
land_boards has 451 orders / 15 reviews
Ships from United States of America
68000 CPU in an FPGA
Altera/Intel Cyclone IV or V FPGA
Video Frame Buffer in SDRAM
Based on TG68
Vasm and GNU GCC Toolchain (Linux)

I recently completed a build of Jeff Tranter's build of the TS2 68000 CPU. It turned out pretty nicely but has serious limitations due to the original memory map of the TS2 which only had 32KB of SRAM from 0x000000-0x007FFF. The PROM is located in the next 32KB space from 0x008000 to 0x00BFFF. Porting the assembly language Tutor ROM code to higher memory proved to be quite painful. 

A Better Target Design

A helpful person in the MOTOROLA 68000 / 68K ASSEMBLY LANGUAGE PROGRAMMING Facebook group pointed me to AMR's TG68 Experiments pages. 

One of the advantages is that this design uses the 32MB SDRAM on typical FPGA cards. 

I made a build of AMR's design targeted to Cyclone IV and V FPGA cards and documented it in a log of the TS2 build (SDRAM Working in a Different Context). In particular, this leverages from Part 14 of the series. If you want to try AMR's project, start with the Master branch here.

The project creates a very powerful 68000 FPGA project and has a good narrative of the progress AMR made in the work but is short on details. Many of these details require digging into AMR's source code. I should note that I opened an issue on his Github page and he was very helpful.

This project will try and fill in some of the gaps in AMRs documentation. 

  • Serial startup message

    land-boards.com02/12/2021 at 10:48 0 comments

    Here's the serial output:

    Conducting sanity check...
    Firmware received - launching
    Heap_low: cef0, heap_size: 1ff2110
    Mouse timed out
    Initialising SD card
    Done - waiting
    Done - waiting
    cmd_CMD8 response: 1
    CMD8_1 response: 0
    CMD8_2 response: 0
    CMD8_3 response: 1
    CMD8_4 response: aa
    Done - waiting
    CMD55 1
    CMD41 1
    Done - waiting
    CMD55 1
    CMD41 0
    CMD58 0
      CMD58_2 c0
      SDHC card detectedSwapping byte order of partition entries
    Start: 8192
    PartitionCount: 1
    Partition: 0  Start: 8192  Size: 62325760
    Read boot sector from first partition
    FindDrive() returnedC024576)
    Changed directory
    Dhrystone Benchmark, Version 2.1 (Language: C)
    Program compiled without 'register' attribute
    Execution starts, 25000 runs through Dhrystone
    Execution ends
    Final values of the variables used in the benchmark:
    Int_Glob:            5
            should be:   5
    Bool_Glob:           1
            should be:   1
    Ch_1_Glob:           A
            should be:   A
    Ch_2_Glob:           B
            should be:   B
    Arr_1_Glob[8]:       7
            should be:   7
    Arr_2_Glob[8][7]:    25010
            should be:   Number_Of_Runs + 10Ptr_Glob->  Ptr_Comp:          35210
            should be:   (implementation-dependent)  Discr:             0
            should be:   0
      Enum_Comp:         2
            should be:   2
      Int_Comp:          17
            should be:   17
            should be:   DHRYSTONE PROGRAM, SOME STRINGNext_Ptr_Glob->  Ptr_Comp:          35210
            should be:   (implementation-dependent), same as above  Discr:             0
            should be:   0
      Enum_Comp:         1
            should be:   1
      Int_Comp:          18
            should be:   18
            should be:   DHRYSTONE PROGRAM, SOME STRINGInt_1_Loc:           5
            should be:   5
    Int_2_Loc:           13
            should be:   13
    Int_3_Loc:           7
            should be:   7
    Enum_Loc:            1
            should be:   1
    Str_1_Loc:           DHRYSTONE PROGRAM, 1'ST STRING
            should be:   DHRYSTONE PROGRAM, 1'ST STRINGStr_2_Loc:           DHRYSTONE PROGRAM, 2'ND STRING
            should be:   DHRYSTONE PROGRAM, 2'ND STRING
    User time: 2920
    Microseconds for one run through Dhrystone: 116
    Dhrystones per Second:                      8561
    VAX MIPS rating * 1000 = 4871

    32 MB of allocated memory (33,497,360 bytes)

  • Demo Video #1

    land-boards.com02/06/2021 at 11:24 0 comments

  • Passing Variables from C to Assembly under GCC

    land-boards.com01/23/2021 at 13:21 0 comments

    I've loved the (on-line GodBolt) Compiler Explorer project for a while now. It lets you type code in one window and see it compiled to assembly language in another window. There's a Compiler Explorer site which does 68k cross compiling. This also lets you play with compiler options like optimization.

    GodBolt Compiler Explorer GCC versions

    The GodBolt tool supports various GCC versions. 

    My GCC compiler toolchain uses version:

    I tried various GCC versions and the results in the below simple case were the same.

    Passing variables from C to assembly functions

    One of the more painful things to grapple with is passing variables from C to assembly language. To understand the passing process, let's look at a simple example. Here's C code which passes two variables to an add function and returns the sum of the two numbers:

    // test passing variables using GCC on the 68K CPU
    int addTwoNums(int a,int b)
        return (a+b);
        int i =- 2;
        int j = 3;

    Here's where the GodBolt Compiler Explorer comes in handy. Let's look at how the GCC compiler handles passing variables between C functions to see what we'd need to do if we write assembly that gets called from C.

            link.w a5,#0
            move.l (8,a5),d0
            add.l (12,a5),d0
            unlk a5
            link.w a5,#-8
            moveq #-2,d0
            move.l d0,(-4,a5)
            moveq #3,d0
            move.l d0,(-8,a5)
            move.l (-8,a5),-(sp)
            move.l (-4,a5),-(sp)
            jbsr __Z10addTwoNumsii
            addq.l #8,sp
            moveq #0,d0
            unlk a5

    Values which are passed from the C function to the assembly language routine relative to the stack. In this example, the first variable, i, is passed at an offset of -4 from the a5 register. The second variable is passed at an offset of -8 from the a5 register.

    The return value from the add function is found in d0.

    The routine is bounded by the link at the start and unlk *unlink" at the end of the routines. These are described in this 68000 Programmers Reference document.

    If we want to write assembly language routines that can be called from C we can mimic this functionality. But there's a rub with the AMR code that adds a tiny bit of complexity (or is it simplification?).

    AMR's C/Assembly Code

    Let's take a look at one of the AMR pieces of assembly code to see what was done for that code.  This code (spi_readsector.s) reads a sector from the SD card and loads it to an address range specified by register a0:

    SPI_PUMP    equ    $81000100
        XDEF spi_readsector
        move.l    4(a7),a0
        movem.l    d2-d7/a2,-(a7)
        moveq    #15,d7
        lea    SPI_PUMP,a1
        move.w    (a1),d0
        movem.l    (a1),d0-d6/a2
        movem.l    d0-d6/a2,(a0)
        add.l    #32,a0
        dbf    d7,.loop
        movem.l    (a7)+,d2-d7/a2

    The code pushes the registers used by the routine at the start onto the stack and restores (via pull) the registers before the return from subroutine. This preserves the registers in the calling routine.

    Take note, though. This code isn't using the link and unlink. It's not using the a5 register as the stack frame for the routine. The reason that the code can do this is found in the gcc options found in the makefile. The option used is: -fomit-frame-pointer. This causes the compiler to not use the frame pointer. Instead it uses the a7 stack pointer directly. Obviously this is more efficient both in speed and memory usage.

    That's also why the assembly language routine looks for the first passed variable at 4 from the a7 register instead of 8 in the GCC example. Adding the -fomit-frame-pointer option to the Godbolt compiler produces the following assembly code:

            move.l (4,sp),d0
            add.l (8,sp),d0
            subq.l #8,sp
            moveq #-2,d0
            move.l d0,(4,sp)
            moveq #3,d0
            move.l d0,(sp)
            move.l (sp),-(sp)
            move.l (8,sp),-(sp)
            jbsr __Z10addTwoNumsii
            addq.l #8,sp
            moveq #0,d0
            addq.l #8,sp

    This code has the same form as AMR's code. The first line in _main reserves 2 longs (8 bytes) for the stacked variables used by the...

    Read more »

  • Fixed Display RAM Accesses

    land-boards.com01/17/2021 at 18:27 0 comments

    The rectangles code wasn't working correctly on the EP4 FPGA card. Luckily, I found the problem relatively easily. Required setting the rows and columns for the SDRAM part that is used on the card. In the C4BoardTopLevel file.

    tg68tst : entity work.VirtualToplevel
      generic map (
        -- W9825C6KH-6 Winbond 4M X 4 Banks x 16 bits SDRAM
        -- 13 rows, 9 columns
        sdram_rows => 13,
        sdram_cols => 9,

     The same fix needs to be done for the Cyclone V card since it used the same SDRAM part.

  • Bootloader and SD Card

    land-boards.com01/16/2021 at 14:08 0 comments

    Boot Process

    When AMR code runs it loads the executable program via a bootloader from the SD card. The bootloader code runs from ROM on the FPGA.

    SD Card Image

    The SD Card contains a single image, boot.sre . This image is loaded by the bootkiader when the card is powered on (or reset) and run automatically.

    The image is an S record.

  • GitHub under VirtualBox

    land-boards.com01/16/2021 at 13:05 0 comments

    Sync up with repository

    git pull

    Move changes to GitHub

    git add .

    git commit 'stuff'

    git push

  • Build on EP4CE15

    land-boards.com01/16/2021 at 11:38 0 comments

    Uses a bit more resources on an EP4CE15 card:

  • Software Build Toolchain

    land-boards.com09/07/2020 at 16:20 0 comments


    • vasm - Assembler
    • Built from source on Linux.

    C Compiler

    • GCC - 68k Cross-compiler
    • Built from source on Linux

    Build environment

    • Run in VirtualBox under Windows.

    Example Software

    Makefile for Rectangles code

    ARCH    = 68000
    BASE    = /opt/m68k-elf/bin/m68k-elf
    CC      = $(BASE)-gcc
    LD      = $(BASE)-gcc
    AS      = $(BASE)-as
    CP      = $(BASE)-objcopy
    DUMP    = $(BASE)-objdump
    VASM    = ../../../vasm/vasmm68k_mot
    # we use crt0.s from here
    STARTUP_DIR = ../../Firmware_Common
    COMMON_DIR = ../../Firmware_Common
    STARTUP_SRC = $(STARTUP_DIR)/startup_app.s  $(STARTUP_DIR)/premain.s
    STARTUP_OBJ = $(patsubst $(STARTUP_DIR)/%.s,$(BUILD_DIR)/%.o,$(STARTUP_SRC))
    COMMON_SRC = vga.c uart.c interrupts.c
    COMMON_OBJ = $(patsubst %.c,$(BUILD_DIR)/%.o,$(COMMON_SRC))
    MAIN_PRJ = Rectangles
    MAIN_C_SRC = rectangles.c
    MAIN_C_OBJ = $(patsubst %.c,$(BUILD_DIR)/%.o,$(MAIN_C_SRC))
    MAIN_S_SRC = draw.s
    MAIN_S_OBJ = $(patsubst %.s,$(BUILD_DIR)/%.o,$(MAIN_S_SRC))
    LINKMAP  = $(STARTUP_DIR)/ldscript_app.ld
    # Commandline options for each tool.
    LFLAGS  = -m$(ARCH) -nostartfiles -Wl,--relax -O6
    # Our target.
    all: $(BUILD_DIR) $(MAIN_PRJ).sre $(MAIN_PRJ).rpt
        rm -f $(BUILD_DIR)/*.o *.elf *.sre *.rpt *.map *.lst *.srec *~ */*.o *.bin
    # Convert ELF binary to bin file.
    %.sre: %.elf
        $(CP) -O srec $< $@
    %.rpt: %.elf
        echo >$@ -n "End of code:\t"
        $(DUMP) -x $< | grep >>$@ _romend
        echo >>$@ -n "Start of BSS:\t"
        $(DUMP) -x $< | grep  >>$@ __bss_start__
        echo >>$@ -n "End of BSS:\t"
        $(DUMP) -x $< | grep >>$@ __bss_end__
        cat $@
    # Link - this produces an ELF binary.
        $(LD) $(LFLAGS) -T $(LINKMAP) -o $@ $+ $(LIBS)
    $(BUILD_DIR)/%.o: %.c Makefile
        $(CC) $(CFLAGS)  -o $@ -c $<
    $(BUILD_DIR)/%.o: $(COMMON_DIR)/%.c Makefile
        $(CC) $(CFLAGS)  -o $@ -c $<
    $(BUILD_DIR)/%.o: %.s
        $(VASM) -Felf -o $@ $<
    $(BUILD_DIR)/%.o: $(STARTUP_DIR)/%.s
        $(VASM) -Felf -o $@ $<
        mkdir $(BUILD_DIR)

  • Software Examples

    land-boards.com09/06/2020 at 21:17 0 comments


    AMR has several code examples. I've added some documentation and comments to the code. If you try them, make sure you are on the master branch of AMR's code. I've also got the code in my Linux repo.


    Prints out strings and numbers on the UART serial port using printf.


    I added these comments to the source code

    // rectangles.c - Draws rectangles on the VGA screen
    // random size rectangles
    // combined with previous rectangle colors
    // scrolls screen vertically as well
    // scrolls on vert sync interrupt
    // very busy screen
    // bootloader leaves debug window up


    Graphics test with sprite under PS/2 mouse control.

  • TG68 Hardware

    land-boards.com09/06/2020 at 14:04 0 comments

    This project is intended to be a software project but a description of the hardware is necessary as a reference to understand the resources available to the software.


    • Runs on Altera Cyclone IV and V FPGA Cards
    • 68000 CPU
    • 32 MB SDRAM
    • Host Serial Connection (USB to Serial) 
    • Video Display Unit (VDU)
      • VGA
      • PS/2 keyboard

    FPGA Cards

    Flow Status    Successful - Sun Sep 06 12:05:04 2020
    Quartus Prime Version    20.1.0 Build 711 06/05/2020 SJ Lite Edition
    Revision Name            SOC
    Top-level Entity Name    C5BoardToplevel
    Family                   Cyclone IV E
    Device                   EP4CE15F23C8
    Timing Models            Final
    Total logic elements     7,984 / 15,408 ( 52 % )
    Total registers          3629
    Total pins               134 / 344 ( 39 % )
    Total virtual pins       0
    Total memory bits        107,520 / 516,096 ( 21 % )
    Embedded Mult 9-bit els  2 / 112 ( 2 % )
    Total PLLs               1 / 4 ( 25 % )

    TG 68000 Core

    • 100 MHz?
    • Wait states
    • 32-bits of addresses connected?

    32MB SDRAM

    Host Serial Interface

    • FT-230XS USB to Serial
      • 115200,N,8,1
    • USB B connector

    Video Display Unit

    • VGA
    • PS/2 keyboard and Mouse

    Memory Map

    • 0x00000000-0x0000FFFF = ROM
    • 0x00100000 - Framebuffer
    • 0x0FFFFA -  Variables
    • 0x7FFFFE - The initial stack pointer
    • 0x800000 = VGA controller
      • Screen base address register
    • 0x810000 = Peripherals
      • Four counters, t0 through t3
      • 0x810010-0x810016 = Divisor register for all four counters
      • 0x81000e = Control word at which contains interrupt enable bits and status bits for counters t1 through t3.  
      • T0 acts as a prescalar for the other three timers, so with the system clock set to 112.5MHz, setting T0’s divisor to 1125 gives the other three timers a 100KHz base clock.
    • 0x820000 = Audio controller

View all 10 project logs

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates