-
Who is afraid of Red, Green and Blue ?
05/15/2024 at 20:17 • 0 commentsA true milestone today ! Just half an hour ago...
The picture shows my test setup, with a laptop, scope, Raspberry-Pi screen, and....
FIRST VIDEO OUTPUT !
It shows just a simple testpattern that I quickly made up. Colored bars and pixel patterns, and when I wrote it I had no idea how it would look. I did not bother to start the pixels at the correct distance from the sync pulse, so apparently I started much too soon since a the right-side, one-third of the screen is empty. (but I also just generated 60 characters instead of 80, to have simpler code).
The picture gets its stability from the timer interrupt on Isetta. The picture stays fully stable if I give Isetta commands via the Raspberry Pi screen. On the Isetta, there is no need for forcing your code in certain blocks with exact equal amount of cycles as in some other designs. (but of course, the interrupt code that generates the picture must have exact timing).
You can see on the picture that the video connector connects with wires to the pcb. That's because I used the wrong footprint for the 15 pin video connector. I had no idea that there were different types. The difference seems to be that one has a row distance of 2 mm and the other one a distance of 2.5 (or 2.54) mm.
Soon there will be characters on the screen !
-
File system and command shell
05/14/2024 at 20:38 • 0 commentsFILE SYSTEM
Isetta has a 32MByte serial flash, and it is there because I want her to have a file system.
While making this file system, I also make a simple command shell, at first just for testing the file system functions but it will grow to get every command that is needed.
The serial flash is a W25Q256JV device. It has 8,192 erasable sectors of 4kByte each.
It would be cool to have a little wear-leveling. That almost rules out a FAT system, where an allocation table is written to the same sector, each time as one of the files changes (unless you write it on a different position each time).
What did I come up with ? The system has the following properties:
- files (that consist of one or more sectors)
- hierarchical directory structure
- deleted or overwritten files stay available (but are normally not visible in the directory), until erased (after adjustable time).
- supports hidden filesFILE STRUCTURE
The first sector of a file (or directory) has this structure:
At 0x0000:
- (byte) flags
- (word) sector of the directory that it belongs to
- (byte) lsb of the properties address
- (byte) hash of the name
- (byte sequence) name (zero-terminated), max ca 100 chars.
At properties:
- (word) number of the following sector
- (word) reserved for more flags (unused yet)
- (4 bytes) date (YYYYMMDD, so today is 20 24 05 14)
- (4 bytes) time (HHMMSS00) (so it is zero-terminated).
- (4 bytes) file size (little-endian)At 0x0100:
- maximal 0x0F00 bytes of dataThe first sector has a lot of space to define more properties. For the sectors that follow, only the flags and the number of the next following sector need to be present. For ease of programming, the data in these following sectors also start at 0x0100.
The flags at address 0 are these ( they are active low ):
- bit0 if 0, sector is ready to be written
- bit1 if 0, sector is in use
- bit2 if 0, sector belongs to a file (but is not first sector)
- bit3 if 0, this is a directory
- bit4 if 0, this file was deleted (but still available)
- bit5 unused
- bit6 unused
- bit7 if 0, this file is hiddenFLASH LAYOUT
Where are the files placed in the flash and how do we find them back ?
There are two rules:
- The first sector of a file will be placed at a position that is based on the directory that it belongs to, and on a hash function of it's name. If that position is occupied, it will start looking for an empty position in one of the following sectors.
- The following sectors of a file can be placed anywhere, since they are all linked.There is only one fixed position, and that is the ROOT directory at sector 1.
DIRECTORIES
The flash is divided in 8 compartments. The files of a certain directory will all be stored in the same compartment. A simple hash function, applied to the sector number of the directory, tells with compartment it belongs to.
FILES
In every compartment there are 1024 sectors. A hash function, calculated from the first and last character of the file name, determines the preferred position of the first sector in the compartment. If the preferred position is already occupied, the first free sector that follows this preferred position will be used.
So, when the file name is known, it can be found very fast, because only the flags, directory, and name of a file have to be checked in each sector. In most cases it is not needed to check the full filename, because checking the hash is sufficient. In such cases only 5 bytes of the sector need to be read.
When the file name is not known (when asking a directory) only a single compartment has to be searched for valid files. Only the flags and the directory sector have to be checked. This is also very fast, when you type a "ls" command you don't notice any delay.
The bytes for date and time have the highest bit set, except the last byte of the time, that is zero. Now we can use the same sort algorithm for names and time/date.
REALIZATION
The file system was built (in Z80 assembler). It uses microcoded functions for reading/writing on the SPI bus. It works. Functions were made to find a file, and to read or write a sector.
Directories can be created (mkdir) and you can change directory (cd).
There is now also a TRANSFER command, that transfers a file from the Raspberry Pi to the Isetta filesystem (in the current directory).Very soon, the loader will, after reset, switch to the BOOT directory, and load a BOOT.BIN and execute it.
For the moment, the binary files will have the Intel-Hex file structure, as discussed in the previous log.
-
Moving Z80 binaries to Isetta and run them
05/07/2024 at 19:48 • 0 commentsNext step is the loader program. It will be a Z80 program, running on Isetta, that uses the console char I/O, so now it is controlled from the RPi console but in the future it can be controlled from PS/2 and VGA.
This loader program will be in the microcode ROMs, and some microcode will download the loader into the RAM soon after reset, and then start the Z80 loader.What must the loader do ?
- download program files from RPi into the Isetta memory, through the 'keyboard interface'
- start a program
- inspect memory and change bytes in memory
- read a file from serial flash (to automatically start software in the future)Need an assembler !
I found a nice online Z80 assembler: https://asm80.com
Written by Martin Maly, Prague: https://hackaday.io/adentIt can also assemble for the 6502, that can also be useful in my project.
The assembler generates Intel format binary (.HEX), like this: (spaces added)
: 10 E000 00 31FEFFC325E04FD3317928FAC9DB3128 2F
: 10 E010 00 FCC97EB7C8CD06E02318F7CD0DE077FE 2A
.....
: 10 E0E0 00 20202020202020202F72756E20617420 97
: 10 E0F0 00 68686868000D4973657474612070726F 98
: 0E E100 00 6772616D206C6F61646572002A00 A9
: 00 0000 01 FFSo the structure is: <:><nr-of-bytes><address><record-type><byte-sequence><checksum>
See: https://en.wikipedia.org/wiki/Intel_HEXThe Javascript microcode generator has now the ability to read this intel hex format, in order to include the loader/monitor in the microcode.
Another change to the Javascript simulator was giving it char I/O according to new KB, TX standard.Now came File transfer from RPi to Isetta, to load the Isetta memory with a Intel Hex file. In the Isetta loader you have to type a "Load File from Host" command, "F" followed by filename. Isetta then sends %Dfilename% (D is the command: download). So '%' is used to introduce the file name and start the download. For just sending a plain '%' to be displayed, it must be doubled to "%%".
The data has to travel over the link to Isetta, and that is not very fast. So I did a simple change, replacing every two hex digits by a single byte. In this new format, it uses '=' instead of ':' at the beginning of a line.
RPi will now send the file. Since the values 00 and FF can not be transported over the link, they are escaped with '['. And because values 00 and FF can occur quite often in machine language, all bytes are first XOR'ed with 0x40, so:
Sending from RPi:
- xor with 0x40
- if 00 replace by <[><A>, if FF replace by <[><B>, if [ replace by <[><[>
(send it)
Receiving at Isetta:
- if [, get another byte, if A then -> 00, if B then -> FF, if [ then -> [
- xor with 0x40
This transformation does not apply to the '=' at the start of the line. and to the 0x0D at the end.The record type, 0 for a normal record and 1 for the end, has many unused values, so that can later on indicate if the bytes must be loaded into another memory bank. TBD.
And the recordtype could be used to store target cpu 6502/z80/... (but it is more practical to define that every program is a Z80 program. A program for 6502 should simply start with an instruction that switches to 6502). Recordtype 0x99 indicates 'file not found'.During transfer, Isetta transmits readable progress messages on the console: at every 128 transferred bytes it displays the current address. It can also terminate the transfer with a special %X sequence (in case of error).
So, now it is possible to assemble Z80 programs online, send the binary to Isetta, and execute it there !
-
The real hardware runs Apple 1 Basic !
04/19/2024 at 19:07 • 0 commentsA major milestone was reached today ! The original Apple 1 BASIC, programmed by WOZ, is running on the real Isetta hardware ! It runs on the intended 12.5 MHz (80nS cycle time).
For character I/O, there is a synchronous serial link with a Raspberry Pi. The screen and the keyboard of the Raspberry Pi are used to communicate with Isetta.
Here you see a screenshot of the RPi screen. After the ">>" (generated by the RPi) the RPi reads a full input line, echoing it on the screen. Then, after a CR (return), the line of characters is sent to Isetta Apple Basic, and Basic again echoes the characters to the screen. Then the output of the Basic interpreter follows, and the Basic prompt ">". Note that there is an "*** END ERR" because there is no END statement at the end of the program.
PROGRAMMER
Quite some work had to be done after the prototype was soldered. I had to make a Python program on the Raspberry Pi to program the microcode into the three microcode flash chips. On the programmer board (described in the previous log) a small change was needed. This Python software can also execute arbitrary microcode on Isetta, and run a test program that tests hardware functions.
The programmer can grab the clock of Isetta, by putting the first 74AC163 clock divider into LOAD mode, and providing a new clock (SW generated by RPi) to the first two preset inputs of the 163. The transistor that transferred this clock was, unsurprisingly, not fast while switching OFF, and I suspected that was the reason that I sometimes got a bad working instruction during singlestepping. This transistor also did the 3.3V to 5V translation. I replaced it by a simple diode with a 1N5 cap in parallel. It's a poor man's level converter. At the high RPi 3V3 level, 0.7 V is added by the diode so the ac163 sees around 4V on its input. That worked, errors now occurred rarely, and could be handled by a retry mechanism in the programmer.
How does the application program (the 4K Basic interpreter) get into the main RAM ? I could modify the programmer such that it writes to RAM instead of microcode flash. But I already had a mechanism in place that would also work with a fully stand-alone power-on and reset. A certain part of the microcode copies the BASIC interpreter program from the microcode flash to the main RAM, soon after reset.
COMMUNICATION
Next thing was communication (characters in, characters out) between RPi and Isetta. It should also work when Isetta generates VGA output, so we are handling a single bit directly after the horizontal interrupt. (That is generated by a simple hardware timer).
The bit handling should always take exactly the same amount of cycles, independent of the data send or received, otherwise there will be jitter in the generated VGA output.When the VGA output and keyboard are working, we still need this interface to the RPi to transfer files to or from Isetta. We could also use the RPi screen as debugging output while an application runs on the VGA screen.
Since it should also be possible that a PS/2 keyboard is connected to Isetta, the choice was made to use the same receiver hardware and software for the keyboard as well as for data coming from the Raspberry Pi. Since it uses open collector outputs, both clock and data signals can simply be tied together. This combination was not foreseen when the PCB's were designed, so a few wires had to be added.
Communication RX receive driver
The keyboard has two lines, KB_CLK and KB_DATA. Both lines are driven by a open collector output on a standard PS/2 keyboard (pullup resistors in Isetta). If no information is sent, both lines are high. The keyboard receive driver will be able to receive scancodes that are sent by the keyboard. It has the following states:
- Wait for startbit. If the clock line has a falling edge, and the KB_DATA line is low, the startbit is valid and we proceed to the next state. If the data line is high at this falling clock edge, we stay in this state.
- Read databit. There are 8 of these states. If the clock line has a falling edge, the data line will be sampled and its value will be shifted into the keyboard-read register. Bit 7 in this register will be the bit that was sent first. This will be repeated 8 times. After that, we will continue to the parity-bit state.
- Read parity bit. If the clock line has a falling edge, the data will be read but it's value will not be used. But the value of the keyboard-register will be transferred to the keyboard buffer. The keyboard buffer can contain 255 values. There is no check if it is full. (Assumption is that the SW works faster than I can type).
- Read stopbit
The keyboard-receive system will also be used to transfer information from the Rasberry Pi to Isetta. The microcode programmer pcb has two open collector outputs for this purpose, that are (on Isetta) directly connected to the two wires coming from the keyboard. On the RPi this uses GPIO27 and GPIO2.
So, with a simple program on the RPi, the RPi keyboard can be used for Isetta. Instead of scancodes, ASCII codes will be sent over the connection. In the future, this ASCII encoding might be slighty changed, so that it does not overlap with scancodes, and Isetta can see if the info comes from a PS/2 keyboard or from the RPi.Note that a PS/2 keyboard sends the LSB first, while the above receive system stores the first received bit in
the MSB (used because it gives simpler microcode). So Isetta receives reversed scancodes.Communication TX transmit driver
The same keyboard driver will also handle traffic in the other direction (This is NOT the system that is used to send information to a PS/2 keyboard ). This uses a third wire called GP_OUT1, that is an output for Isetta and an input for the RPi. It is called GP_OUT1 because it can be used for other purposes when this TX function is not used.
The output is synchronous, clocked by the KB_CLK signal that is generated by the RPi. It requires that the RPi sends a keyboard byte in order to receive the TX information. The RPi must send a low startbit, and if there is no key to send, the databits should be all ones so Isetta sees a FF character and can dismiss it. Sending FF also provides a good synchronisation on the startbit.
In Isetta, data to transmit must be put into the transmit buffer. It can hold 255 characters.
The keyboard states have the following functions for TX:- Wait for startbit. If the clock line has a falling edge, and the KB_DATA line is low, the startbit is valid and we proceed to the next state. If the data line is high at this falling clock edge, we stay in this state. The tx-register is loaded from the transmit buffer, but if the transmit buffer is empty, the tx-register is set to FF. The empty/non_empty state will be remembered in a tx_state register. So, if there is no data to send to the RPi, the RPi will receive a FF byte.
- Read databit. There are 8 of these states. If the clock line has a falling edge, the tx-register will be shifted left, and the bit that is shifted out will be put on the GP_OUT1 line. This will be repeated 8 times. After that, we will continue to the parity-bit state.
- Read parity bit. If the clock line has a falling edge, it will, if tx_state is 0, increment the pointer to the transmit buffer.
- Read stopbit
In each line interrupt, this RX+TX code will take approx. 16 cycles.
Changes to Apple Basic
Apple Basic uses memory locations for its character I/O. In Isetta, the 8080/Z80 IN/OUT instructions
will be used for I/O. These IN/OUT instructions were also added to the instruction set of the 6502 (with exactly the
same opcode as on the 8080/Z80, possible because 6502 has many unused opcodes).At this moment, port 0x31 is used for RPi/KB input as well as output.
- IN 0x31 ; Reads KB scancode or input from RPi. Returns byte in ACC. When no byte available, returns ACC=0 with Z_flag=1.
- OUT 0x31 ; Writes character in ACC to RPi, and when the buffer is full, returns ACC=0 with Z_flag=1.
addr = 0xe3d5; // address where bytes must be changed in output routine memory[addr++] = 0xD3; // OUT (opcode 0xD3) memory[addr++] = 0x31; // port 0x31 memory[addr++] = 0xF0; // BEQ (try again if buffer was full) memory[addr++] = 0xFC; // -4 memory[addr++] = 0x60; // RTS addr = 0xe003; // address where bytes must be changed in input routine memory[addr++] = 0xDB; // IN (opcode 0xDB) new keyboard-input instruction memory[addr++] = 0x31; // port nr memory[addr++] = 0xF0; // BEQ, loop while buffer is empty memory[addr++] = 0xFC; // -4 memory[addr++] = 0x09; // ORA #80 memory[addr++] = 0x80; // memory[addr++] = 0x60; // RTS, char in acc.
A lot more to do, a few things:
- Getting VGA output to work
- Make a simple file system in the 32Mb serial flash
- Get PS/2 keyboard working
I bought a PS/2 keyboard online for about 20 Euro, but it keeps sending the same data at fixed intervals. A little online searching learned that my keyboard model only works after it receives a reset command. So that will be a small hardware and software change to add transmitting a reset message to the keyboard.
Supporting a PS/2 Mouse would also be nice...
-
PCB and parts arrived !
03/11/2024 at 14:16 • 0 commentsI received the PCB's ! These PCB's were sponsored by the kind people of PCBWAY. As always, they are of a very good quality.
And I also received a big bag of parts:
Warm up the soldering iron !
-
PCB finished, Logisim simulation, and programmer
02/22/2024 at 20:25 • 0 commentsLOGISIM simulator
The design was only tested on the logical level, in the Javascript simulator. Before making a PCB I wanted some form of confirmation more on the hardware level, so I entered the design in the good old Logisim.
Nowadays there is a further developed version, called Logisim Evolution. I tried it by loading one of my old designs in it. It became a big mess with long hex-sequences appended to my net names, and many shapes not displaying correctly. And indeed, Evolution is no longer compatible with the old file format. That's a pity, because I had quite a lot TTL devices converted to Logisim circuits.
So I stayed with the old version, 2.7.1, so I could use my library of TTL components. And I found a few mistakes in my design. Also found a way to save a chip on the generation of the horizontal sync pulse.
But it is slow. At it's top speed (Logisim says it is 4100 cycles/sec), when you type a short line of Apple Basic, you have to wait one minute before the prompt reappears ! Basic doesn't even execute anything, it only stores the line !
PCB Finished
I finished PCB design. Here is the nice 3D preview of KiCad:
It is 119 x 150 mm (4.7 x 5.9 inch), 4 layers.
Microcode Programmer
I also needed a programming interface. Previous projects used the Raspberry Pi for that. The Pi has enough GPIO pins to accommodate the 8 or 16 bit wide Flash memories of those projects. The interface was not much more than a small universal board, with resistors for 5-to-3.3 volt conversion.
But in the Isetta the microcode flash memory is 24 bits wide. That is almost all I/O that the Pi has, not leaving enough for control signals. So now I use 8 bits I/O from the Pi. The data going to Isetta is first transferred to three HCT574-type registers (the HCT will also translate 3.3V to 5V), and then it can be transferred as one 24 bit value to Isetta. For data going to the Pi, there are three LVC244 chips to convert 5V to 3.3V. Only one of the LC244 is enabled to transfer its value to the Pi. There are two more LVC244, one for the databus and one for control signals from Isetta to the Pi.
The new RPi programmer is only required once, to program the microcode. The program for the 6502 or Z80 can reside in RAM, and can be programmed in BASIC or any other convenient language.
The Raspberry Pi has the following control outputs to Isetta:
- Disable the data output of the 3 Flash chips
- Give a programming pulse to the 3 Flash chips
- Take control of the Isetta CPU clock signal
- When it has control, send a clock to the Isetta CPU (to single-step it).
The RPi also has a few serial lines to/from Isetta, intended for character read/write so you can use the RPi as a terminal as long as Isetta has its video and keyboard interface not yet working.
Here is how it looks. Isetta connects to the left side, and the RPi to the right side. It also has LED's for the 24 microcode signals (with explanation on the pcb), and LED's for the databus and the instruction register. The LED's will also work if the RPi is not connected. It is a 2-layer board.
-
PCB design progress
02/14/2024 at 14:22 • 0 commentsHere is the current state of the design, it's almost finished. The Flash chips had to change from DIL to PLCC, because the required 55nS speed is not available in DIL.
-
Re-started PCB design, in SMD
01/28/2024 at 09:20 • 0 commentsSince I last worked on the PCB design, there have been many changes.
And after looking at the availability of the chips, I found that several of them were not in stock (in DIL version). And since the design has become faster, it is not as easy any more to switch between the TTL logic families, because for many chips, the speed is critical.
This made me decide to switch to SMD devices. Only the microcode flash chips will stay in DIL, so they can be easily taken out to program them (although there will be a system to program them while they stay on the board).
Here is a preview of the new design:
Dimensions are 12 x 15 cm ( 4.7 x 5.9 inch). It will be a 4-layer board. It is designed to fit in a Hammond RM2055S enclosure.
-
Less parts and higher speed
12/04/2023 at 20:36 • 0 commentsIt's been a while since I wrote a log. In this log I will tell you what happened to the design in the past months.
BYPASS
The design has a few buffers that implement the "bypass". This can place the address registers on the databus, so we can inc/dec them, or store them to T and then to memory. This is also the path to store the program counter (pch/pcl) for a CALL instruction. And it is used to add the offset to the PC in case of a branch.
As always, I was looking for components to remove, to get a low-component-count design. And indeed, we can do without the buffer for the low address bus ! We can simply put a 1-to-1 table in a zero page of memory, and an access to this table will give us the register value on the databus. The high address bus must be 0 to access this table, that it is already an existing function.
We have 8 memory banks, so there is a maximum of 8 zero pages, enough space in zero page. One of the zero pages already has a table for right-shift.
For the high byte, this is less useful, since the table would be scattered through memory, occupying one byte in every page. That is not a good idea.
But... the only register that we need from the high bus is PCH. And the PCL bus is not loaded from the result bus, but from the DPH register (in this picture, that is. It was later changed to come from the T register).
If we modify it, and load the PCL register from the PCH register, we can access the PCH contents by loading it into PCL first ! In fact, when we write a new value to PCH we will at the same time (with the same signal) write the old PCH contents to PCL.
For doing a jump, first write the LSB to PCH, and then write the MSB to PCH (that will move the LSB from PCH to PCL). And for a CALL, first save the PCL address through the 1-to-1 table, via the ALU, to T. T is then stored to the stack. After the LSB of the subroutine address has been written to PCH, the old PCH is in PCL and can now also be saved to stack.
The control signal for the bypass is obviously no longer needed. Used it later to double the amount of RAM-based registers.
The 1-to-1 table in RAM is built by microcode, directly after reset, in the same loop that fills the right-shift table.
Changes for this were done in the simulator and the microcode generator.
SPEED
In october I did some timing calculations, using the typical values for 74AC logic. (These typical values are only in Fairchild datasheets, not in the Texas Instruments datasheets).
I was a bit surprised to see, that with some measures, the clock speed could be doubled to 12.5 MHz !
We would need a 10nS RAM. Circuits in the address bus needed a few changes to make it faster. One of the address signals (for the CGL register) came from a 74HC238 1-to-8 decoder that had a quite big delay. It was used to extend the number of memory-based registers. This way of extending the number of registers was dropped (replaced by a directly available microcode control bit). This HC238 also delivered a signal to select SUB and a signal to select DEC in the ALU. This could be changed to a single signal for both SUB and DEC. Other functions in the HC238 could also be solved in a different way. The HC238 could be removed.
The circuits for the flags had to change, in the design the CY-out of the adder sometimes had to pass four devices before a flag result could be clocked-in in its register. The flag system was changed such that the adder-cy-out is used at only a few places. The condition-selector can choose it, and in a group-1 instruction it can be put into the C flag. The adder-cy-out now only has to pass 2 devices. The last of these is a 74HC4053 analog switch, that is slow in switching to the other channel, but relatively fast to pass a signal. Since the switch channel can be setup at the start of the cycle, it is expected to be fast enough.
The adder-cy-out signal is called TC (temporary carry) in the microcode. For group 1 instructions, this signal is stored in a register, to make this signal accessible to later microcode instructions, this register output is called DTC (delayed temporary carry). This signal DTC can now also be chosen by the condition selector (after shuffling some of its inputs).
Today I uploaded a new schematic to the file section, that also mentions the timing of the relevant signals. The critical path takes a calculated 74 nS, so that will be fast enough for the new 80 nS cycle time.
Changes were needed in the simulator and the microcode.
There were several other changes, too many to mention them all.
-
Colors
08/24/2023 at 18:34 • 1 commentThis computer will have 64 colors (2bits red, 2 bits green, 2 bits blue).
I was curious what the colors would look like, so I made a kind of three-dimensional plot with dimensions red, green and blue. It became a cube, built from 64 small bricks. If we look at the cube with the black brick pointing to us, we see this:
So, starting from the black brick going to the right, we see increase in the red component. To the left, we see increase in blue component, and from black going down we see increase in green component. At the circumference we see the bright rainbow colors, and the remaining colors are darker versions of the colors.
At the opposite side of the cube (unvisible in the above picture), we have a point where the amount of all primary colors is maximal. That is the color white. If we look at the other side of the cube we see this:
At the circumference we see the same bright rainbow colors again. The remaining bricks are the lighter versions of the rainbow colors.
But we did not see everything ! At the inside of the cube, there are 8 bricks that we didn't see yet. These are light-grey, dark-grey, and a grayish version of the primary colors:
So here we see the eight hidden bricks, shown from both sides.
The following table shows these colors, numbered from 0 to 63, and also with their hexadecimal web color. The first column has the grayish colors, and black, white and the two greys are at the bottom:
So this table will be a help if I ever come so far to program colored stuff for my contraption.