I finally tooks some time to clean-up my project and code and publish it on github.
In addition to refactoring the code I also added features :-)
1) Interrupt service routine for RTC. (enabled periodic interrupts from RTC)
2) RAM bank switching.
3) RTC API moved to EPROM.
4) KBHIT implementation.
...and more.
Getting closer to the next development step which is to build general purpose I/O card for my system and connect some peripherals to it. I'd like to have a local standard console for UI instead of UART only. I want to connect some mass storage device to my system, some joysticks etc.
I have quite long TODO list for this project, with many goals completed but some more difficult ones still waiting in queue.
Some of them I list here:
* Write library/API for mass storage.
* Add copying memory ranges to enhanced shell app.
mcp ADDR_FROM ADDR_TO ADDR_DEST
* Build a prototype of a PIA or VIA I/O card on a breadboard. Connect to I/O slot #3.
Create circuit schematic in the ExpressSCH program.
Build on a breadboard.
Test by attaching LED-s and switches to the I/O pins and write program to read/write values to the port.
* Implement online (monitor) assembler and disassembler.
NOTE: To be finally able to write programs in symbolic assembly directly on the MKHBC-8-Rx computer.
* Add detection of RTC / Banked RAM card presence or lack thereof.
NOTE: This should be easy. Method one - assume that RTC and Banked RAM are one. If memory test at $8000 - $BFFF range fails, there is no RTC as well. If it succeeds - assume RTC is also present. More difficult but also more accurate check would be to test RAM in the banked range and separately attempt to read the status register from RTC and determine each of these devices as separate resources.
Also it would be nice to automatically detect that RTC is not set (e.g.: when battery fails or is not present) and ask user to setup time at startup.
Currently with detection not implemented, system still boots up and works. Commands that rely on RTC or banked RAM don't freeze or crash, they just display incorrect information. I suspect that possibly commands 'rclk' or 'sleep' might possibly freeze without RTC/BRAM card present. I did not test these two. I will update this file once I do.
* Add detection of UART card or lack thereof. Add detection of standard console (keyboard, graphics card) or lack thereof.
NOTE: This may be more complicated than RTC / Banked RAM detection card. Bear in mind that this is currently the primary I/O for UI. No UART therefore means - no user interaction with MOS (monitor). However later there will be console present (keyboard, graphics/screen card) and UART will not be a necessary device.
Summary of the system programming as of today:
System programming / Operating System of MKHBC-8-Rx is divided into 2 parts:
- Firmware, which resides in EPROM.
- System programs, which are loaded to RAM via serial port.
Firmware consists of hexadecimal machine code monitor, hardware drivers code, MOS-6502 mandatory vectors: Reset, NMI, IRQ and Kernel Jump Table.
Firmware is implemented in MOS 6502 assembly language.
Kernel Jump Table is a series of jump commands that redirect to code performing certain system functions.
These jump commands are guaranteed to be always at the same memory locations, so the system programs using them don't have to be rebuilt each time the implementation of firmware is changed and some internal addresses has moved. The jump table is always in the same place jumping from the same locations to the same functions that they should perform, even if the functions themselves change the location in the EPROM due to code refactoring or relocation of binary code.
The new entries can be added to the Kernel Jump Table though, so it is guaranteed to be backwards compatible.
Theory of operation:
When computer is started, the reset circuit holds the reset line low long enough for CPU to initialize, then the CPU performs startup routine which consists of reading the Reset vector from the EPROM memory (which must be at fixed address) and executes the routine pointed by that vector.
The routine contains initialization code for OS and then goes into the eternal loop which sends the output via serial port and expects input on the serial port. By connecting text serial terminal device configured to be the same speed as computer's UART speed, it is possible to interface with the computer to send commands and receive output.
The command UI is very simple and consists of very rudimentary hexadecimal machine code monitor which allows to read/dump values in computer's memory, write/modify values in Random Access Memory and execute code at provided address.
This UI is rudimentary but sufficient for entering code into computer's RAM and executing it.
Programming API / Kernal Jump Table:
Function | Address (hex) | Parameters | Return | Description |
CallDS1685Init | FFD2 | RegB, RegA, RegXB, RegXA | RegC in Acc | Initialize RTC chip. |
CallDS1685ReadClock | FFD5 | n/a | Data is returned via hardware stack. Calling subroutine is responsible for allocating 8 bytes on stack before calling this function. Clock data are stored in following order below the subroutine return address: seconds, minutes, hours, dayofweek, date, month, year, century. Valid return data on stack only if Acc > 0 (Acc = # of bytes on stack). Calling subroutine still should de-allocate stack space by calling PLA x 8 after reading or discarding returned data. | Read RTC clock data. |
CallDS1685SetClock | FFD8 | Parameters are passed via hardware stack: seconds, minutes, hours, day of week, day of month, month, year, century. Calling subroutine is responsible for allocating 8 bytes on stack and filling the space with valid input data before calling this function. Calling subroutine is also responsible for freeing stack space (PLA x 8). | n/a | Set date/time of RTC chip. |
CallDS1685SetTime | FFDB | Parameters are passed via hardware stack: seconds, minutes, hour. Calling subroutine is responsible for allocating space on stack and filling it with valid input data before calling this function. Calling subroutine is also responsible for freeing stack space (PLA x 3). | n/a | Set time of RTC chip. |
CallDS1685StoreRam | FFDE | BankNum, RamAddr, RamVal | n/a | Store a value in non-volatile RTC memory bank. |
CallDS1685ReadRam | FFE1 | BankNum, RamAddr | value in Acc | Read value from non-volatile RTC memory bank. |
CallReadMem | FFE4 | PromptLine (contains hexadecimal address range) | n/a (output) | Machine code monitor function - read memory. |
CallWriteMem | FFE7 | PromptLine (contains hexadecimal address and values) | n/a (memory is modified) | Machine code monitor function - write memory. |
CallExecute | FFEA | PromptLine (contains hexadecimal address) | n/a (code is executed) | Machine code monitor function - execute code in memory. |
CallGetCh | FFED | n/a | Character code in Acc | Standard I/O function - get character. |
CallPutCh | FFF0 | Character code in Acc. | n/a (output) | Standard I/O function - put/print character. |
CallGets | FFF3 | n/a (input) | PromptLine, PromptLen | Standard I/O function - get string. |
CallPuts | FFF6 | StrPtr | n/a (output) | Standard I/O function - put/print string. |
CallBankRamSel | FFCF | Banked RAM bank # in Acc. (0..7) | n/a (selects RAM bank, updates shadow register in RAM) | Banked RAM bank selection. |
CallKbHit | FFCC | n/a | Character in Acc or 0 if buffer empty. | Check if there is character in RX buffer (equivalent of check if key was pressed since this is UART I/O). |
WARNING:
Disable interrupts before calling any RTC function:
SEI
<call to RTC API>
CLI
Registers, buffers, memory:
RTC RAM shadow:
RegB = $F6
RegA = $F7
RegXB = $F8
RegXA = $F9
RegC = $FA
Temp = $FB
BankNum = $FC
RamAddr = $FD
RamVal = $FE
UART Pointers
UartRxInPt = $F2 ; Rx head pointer, for chars placed in buf
UartRxOutPt = $F3 ; Rx tail pointer, for chars taken from buf
Uart Queues (after stack)
UartTxQue = $200 ; 256 byte output queue
UartRxQue = $300 ; 256 byte input queue
MOS Prompt variables
PromptLine = $80 ; Prompt line (entered by user)
PromptMax = $50 ; An 80-character line is permitted ($80 to $CF)
PromptLen = $D0 ; Location of length variable
MOS I/O Function variables
StrPtr = $E0 ; String pointer for I/O functions
Other variables:
Timer64Hz = $E2 ; 4-byte (32-bit) counter incremented 64 times / sec
; $E2,$E3,$E4,$E5 (unsigned long, little endian)
Customizable jump vectors
Program loaded and run in RAM can modify these vectors
to drive custom I/O console hardware and attach/change
handler to IRQ procedure. Interrupt flag should be
set before changes are applied and cleared when ready.
Custom IRQ handler routine should make a jump to standard
handler at the end. Custom I/O function routine should
end with RTS.
StoreAcc = $11 ; Temporary Accumulator store.
IrqVect = $0012 ; Customizable IRQ vector
GetChVect = $0014 ; Custom GetCh function jump vector
PutChVect = $0016 ; Custom PutCh function jump vector
GetsVect = $0018 ; Custom Gets function jump vector
PutsVect = $001a ; Custom Puts function jump vector
I/O space / address range:
$C000 .. $C7FF, 8 pages (8 x 256 bytes):
Internal (non-buffered) I/O bus:
$C000 .. $C0FF - slot 0 (RAM bank switching register)
$C100 .. $C1FF - slot 1 (RTC registers)
$C200 .. $C2FF - slot 2 (Reserved for Prioritized Interrupt Controller)
$C300 .. $C3FF - slot 3 (Reserved for built in I/O parallel interface PIA or VIA)
External (buffered/expansion) I/O bus:
$C400 .. $C4FF - slot 4 (UART)
$C500 .. $C5FF - slot 5
$C600 .. $C6FF - slot 6
$C700 .. $C7FF - slot 7
RAM bank switching.
NOTE: Because RAM bank switching hardware register is write only, we cannot read from it to determine
which bank is selected. The purpose of bank# RAM register at $E6 is just that - remembering last
selected bank#.
Address: | $C000 |
Value: | $00 .. $07 |
Banked memory: | $8000 .. $BFFF |
Bank number RAM register: | $E6 |
Memory map:
$0000 - $7FFF: Base RAM, 32 kB.
$6000 - $7FFF: Optional Video RAM, 8 kB. (takes away from Base RAM, leaving 24 kB for general purpose)
$8000 - $BFFF: Banked RAM, 16 kB space x 8 banks = 128 kB.
$C000 - $C7FF: I/O space, 8 slots x 256 Bytes = 2 kB.
$C800 - $FFFF: EPROM, 14 kB.
System programs:
System programs currently consist only one - enhanced shell.
It is written in C and compiled with CC65.
Programs written in C (CC65) or CA65 assembly for MKHBC-8-Rx computer / MKHBC OS use library written in C and assembly languages which implements standard C library (CC65), I/O console and RTC functions and are compiled into library archive mkhbcos.lib.
Corresponding C header files are:
- mkhbcos_ansi.h - ANSI terminal API
- mkhbcos_ds1685.h - DS1685 RTC API
- mkhbcos_lcd.h - standard LCD 16x2 API
- mkhbcos_ml.h - machine code monitor API
- mkhbcos_serialio.h - serial I/O API
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.