Skip to content

Commit

Permalink
Merge pull request #1 from GmEsoft/develop
Browse files Browse the repository at this point in the history
Going public
  • Loading branch information
GmEsoft authored May 24, 2021
2 parents f1ce842 + 227fdec commit 44ffa4a
Show file tree
Hide file tree
Showing 76 changed files with 15,579 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@
*.exe
*.out
*.app

# Junk
*.tws
88 changes: 87 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,88 @@
# Z80-MBC2_VGA32
Z80-MBC2 Emulator running on TTGO ESP VGA32


## Z80-MBC2 Emulator running on TTGO ESP VGA32

This is a Z80-MBC2 emulator running on a TTGO ESP VGA32 board,
an ESP32 device with a VGA connector, PS/2 keyboard and mouse
connectors, an audio output jack and a Micro-SD card connector.

This emulator runs a Z-80 processor at ca. 5.7 MHz (without throttling).
The emulated Z80-MBC2 system is internally connected to the FABGL terminal,
emulating an extended color ANSI terminal.

It has a terminal configuration menu, accessible when the USER button is pressed
on boot. The configuration menu has the following options:
- `[D]isplay mode`: select the VGA resolution and color depth;
- `[K]eyboard layout`: select an international keyboard layout;
- `[T]ime settings`: automatic clock set-up on boot, via an Internet time server;
- `[W]i-Fi settings`: select a Wi-Fi network, used to set the clock.

The Z80-MBC2 configuration menu is also accessible using the USER button.
The menu has been modified from the real Z80-MBC2 system:
- `6: set Z80 clock speed`, changed to `6: Change user debug mode` - to invoke the simple
debugger by pressing the USER button while the emulated Z80 is running;
- `9: change RTC time/date`, changed to `9: Change auto-boot mode` - to choose between
always invoking this menu on start-up or only via the USER button.


## Build

To build this sketch, the following items are needed:
- Espressif ESP-32 hardware support tools and libraries for
the Arduino IDE, either integrated using the "Additional
Boards Manager URL":
<https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json>
or by cloning the GitHub repo or downloading the ZIP from
<https://github.com/espressif/arduino-esp32> (be sure to
select the release branch "release/v1.0";
- FabGL library from Fabrizio Di Vittorio from GitHub:
<https://github.com/fdivitto/FabGL.git> or my fork, which
also contains support for the Belgian keyboard layout:
<https://github.com/GmEsoft/FabGL.git>.

In Arduino IDE v1.8.x, in Tools - Board:, select the "ESP32 Dev Module"
board. In the configuration settings, select the following:
- Partition Scheme: "Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS)";
- PSRAM: "Disabled".


## Run

To start this emulator, get a FAT32-formatted Micro-SD card, copy all files from
the original Z80-MBC2 card image onto it, insert the card in the VGA32 module's
SD connector. Connect a VGA monitor to the VGA blue connector, a PS/2 keyboard
to the PS/2 purple connector, and an USB power supply to the Micro-USB connector.


## Credits

Thanks to the Z80-MBC2 Just4Fun project and its author SuperFabius - Fabio Defabis:
<https://hackaday.io/project/159973-z80-mbc2-a-4-ics-homebrew-z80-computer>
and <https://github.com/SuperFabius/Z80-MBC2>.

Also thanks to the FabGL library from Fabrizio Di Vittorio that turns an ESP32
embedded system into a smart VGA terminal with keyboard, mouse and audio output,
and a Micro-SD card slot: <https://github.com/fdivitto/FabGL>.


## GPLv3 License

Created by Michel Bernard ([email protected]) - <http://www.github.com/GmEsoft/Z80-MBC2_VGA32>
Copyright (c) 2021 Michel Bernard.
All rights reserved.

This file is part of Z80-MBC2_VGA32.

Z80-MBC2_VGA32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Z80-MBC2_VGA32 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Z80-MBC2_VGA32. If not, see <https://www.gnu.org/licenses/>.
180 changes: 180 additions & 0 deletions Z80-MBC2_VGA32/Allocator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/*
Z80-MBC2_VGA32 - Simple Memory Allocator
Created by Michel Bernard ([email protected])
- <http://www.github.com/GmEsoft/Z80-MBC2_VGA32>
Copyright (c) 2021 Michel Bernard.
All rights reserved.
This file is part of Z80-MBC2_VGA32.
Z80-MBC2_VGA32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Z80-MBC2_VGA32 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Z80-MBC2_VGA32. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once

#include "log.h"

#include "HardwareSerial.h"

class Allocator
{
private:

enum Status
{
BOOKED = 0xA10CA7ED, // Allocated
FREE = 0xF2EEF00D // Free food
};

struct Header
{
Header *prev_,
*next_;
size_t size_;
int status_;
//char ptr_[0];
};

public:

Allocator( void *pool, size_t size )
: pOrigin_( (Header*)pool ), initSize_( size )
{
}

void *malloc( size_t size )
{
if ( initSize_ )
{
pOrigin_->prev_ = pOrigin_->next_ = pOrigin_;
pOrigin_->size_ = initSize_ - sizeof( Header );
pOrigin_->status_ = FREE;
LOGD( "New allocator for size 0x%X at %p", pOrigin_->size_ , pOrigin_ );
debug( "Origin", pOrigin_ );
initSize_ = 0;
}

LOGD( "Allocating 0x%x bytes from pool %p", size, pOrigin_ );

Header *ptr = pOrigin_;
do {
ptr = ptr->prev_;

debug( "Candidate", ptr );

if ( ptr->status_ == FREE ) {
debug( "Free", ptr );
if( ptr->size_ >= size + sizeof( Header ) ) {
debug( "Taking", ptr );
Header *pNewFree = (Header*)( (char*)ptr + size + sizeof( Header ) );
pNewFree->prev_ = ptr;
pNewFree->next_ = ptr->next_;
pNewFree->size_ = ptr->size_ - size - sizeof( Header );
pNewFree->status_ = FREE;
ptr->next_ = pNewFree;
ptr->size_ = size;
ptr->status_ = BOOKED;
pOrigin_->prev_ = pNewFree;
debug( "New", ptr );
debug( "Prev", ptr->prev_ );
debug( "Next", ptr->next_ );
check();
LOGD( "Returning %p", ptr + 1 );
return (void*)( ptr + 1 );
}
} else if ( ptr->status_ != BOOKED ) {
LOGE( "ABEND! Memory Pool Corrupt at %p", ptr );
Serial.printf( "ABEND! Memory Pool Corrupt at %p", ptr );
for (;;);
}
} while( ptr != pOrigin_ );

return 0; // malloc failed
}

void free( void *pFree )
{
Header *ptr = (Header*)pFree - 1;

if ( ptr->status_ == FREE ) {
LOGE( "ABEND! Already free pointer: %p", pFree );
Serial.printf( "ABEND! Already free pointer: %p", pFree );
for (;;);
} else if ( ptr->status_ != BOOKED ) {
LOGE( "ABEND! Memory Pool Corrupt at %p", ptr );
Serial.printf( "ABEND! Memory Pool Corrupt at %p", ptr );
for (;;);
}

ptr->status_ = FREE;
debug( "free", ptr );

// merge with next block if free
if ( ptr->next_ != pOrigin_ && ptr->next_->status_ == FREE ) {
LOGD( "Merging with next, deleting next" );
debug( "Delete", ptr->next_ );
ptr->next_ = ptr->next_->next_;
ptr->next_->prev_ = ptr;
ptr->size_ += ptr->next_->size_ + sizeof( Header );
debug( "Prev", ptr );
debug( "Next", ptr->next_ );
}

// merge with previous block if free
if ( ptr != pOrigin_ && ptr->prev_->status_ == FREE ) {
LOGD( "Merging with next, deleting this" );
debug( "Delete", ptr );
ptr->prev_->next_ = ptr->next_;
ptr->next_->prev_ = ptr->prev_;
ptr->prev_->size_ += ptr->size_ + sizeof( Header );
debug( "Prev", ptr->prev_ );
debug( "Next", ptr->next_ );
}

check();
}

void debug( const char *name, const Header *header )
{
LOGD( "%-10s: %p prev=%p next=%p size=0x%x status=%x", name, header, header->prev_, header->next_, header->size_, header->status_ );
}

void check()
{
LOGD( "Checking block chain" );
Header *ptr = pOrigin_;
int i = 0;
do {
String block( "Block " );
block += i;
debug( block.c_str(), ptr );
if ( ptr->next_->prev_ != ptr )
LOGE( "Error: ptr->next_->prev_ (=0x%x) != ptr (=0x%x)", ptr->next_->prev_, ptr );
if ( ptr->prev_->next_ != ptr )
LOGE( "Error: ptr->prev_->next_ (=0x%x) != ptr (=0x%x)", ptr->prev_->next_, ptr );
if ( ptr < ptr->next_ && ptr->status_ == FREE && ptr->next_->status_ == FREE )
LOGE( "Error: unmerged free blocks %p & %p", ptr, ptr->next_ );
ptr = ptr->next_;
++i;
} while ( ptr != pOrigin_ );
}

private:
Header *pOrigin_;
size_t initSize_;
};
Loading

0 comments on commit 44ffa4a

Please sign in to comment.