-
Blueprints and source code now on github.
02/07/2018 at 05:38 • 0 commentsI 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
-
Firmware for PropTermMK is now on Github.
06/20/2017 at 20:01 • 0 commentsI published current version of firmware for my PropTermMK device on github: https://github.com/makarcz/proptermmk
-
NV RAM access added in DS1685 driver.
06/19/2017 at 18:31 • 0 commentsI enhanced DS1685 RTC driver by adding API to access NV RAM.
Details on my blog: http://6502cpu.blogspot.com/2017/06/update-on-mkhbc-8-rx-nv-ram-access.html
-
Finished building the RTC / Banked RAM card.
06/14/2017 at 05:02 • 0 commentsUfff! Took me 2 weeks, but finally finished my combined RTC and banked RAM card. As usual I used prototyping PCB and point to point soldering using enameled magnet wire. I used this part of project as opportunity to enhance my workbench with better soldering tools - quality soldering station, better wire cutters, PCB holder, solder wick, chisel tips, you name it. The job was more enjoyable thanks to that. I had a little scare at the end because the card didn't work right away, but it turned out to be a missed connection. Fixed it and it works now. I am pleased with the result and I'm looking forward to design and implementation of the User Interface I/O card. (keyboard, display, joystick, sound, mass storage etc.)
Almost 300 soldering points - sometimes it is not fun. It is important to have good quality tools for this kind of work. I know, I am not good at it yet, but I am improving.
This PCB holder proves to be a very handy tool.
Ready to populate with IC-s and battery. Taking this opportunity to show off my new Hakko soldering station :-)
The card is in the mother board here, slot B2, working fine.
The test above shows that timekeeping and memory banking is working. Memory bank selection register is at address $C000, banked RAM starts at $8000. I also powered the computer down and back up and the RTC keeps the time. in a few days I will know if it is accurate.
-
Working on the RTC/BRAM PCB.
05/31/2017 at 06:34 • 0 commentsI finally started working on the RTC/Banked RAM PCB. The technique used as on previous cards - point to point soldering on a prototype board. Lots of soldering, but now I have a gadget that makes this kind of work a bit easier and more pleasant.
See for yourselves:
-
Serial port shield for Parallax Human Interface board.
05/21/2017 at 05:08 • 0 commentsSome time ago I tried to make the serial port shield for my Parallax Human Interface board. The Human Interface Board uses many of the Propeller's I/O pins for VGA, sound, mouse, keyboard and SD card. However it still leaves pins P0-P7, and they are available on a header on top of the board along with 5V, 3.3V and Ground pins. Originally I used transistor based circuit from Propeller manual which was recommended by Parallax for programming the chip via RS-232, but unfortunately it didn't work for me. I spend hours debugging, building the equivalent on a bread board, making modifications and experimenting, but all to no avail. Here is the circuit that didn't work for me:
This is how my shield looked like originally, inserted here on top of the Human Interface board:
After many hours lost trying to make it work, I gave up, tested a different much simpler circuit and de-soldered the components to make a new one:
This works just fine except occasional contact issues on the jumpers. I guess I need new jumpers because these are scavenged from some old board and seem to be loose and not making a good contact. I could have made permanent connections instead of reconfigurable jumpers, however I wanted to be able to re-configure hardware connections of Rx/Tx serial port pins to a different Propeller I/O pins if needed. I think this makes this simple shield much more practical.
My development system just got a little bit better.
After all that hard work, I fired up my MKHBC-8-R2 computer to have some fun playing BASIC game on my new shiny Propeller based terminal:
See you later, I have a kingdom to run :-) -
Propeller based serial terminal device with SD card as program storage.
05/16/2017 at 07:08 • 0 commentsThis little computer ultimately is going to be a standalone system. I am working towards this goal to hopefully one day cut the cord and get rid of the need for a PC as a host computer / cross-development platform. In the recent months I have been working on sort of an off-shot project that one day will help me complete this plan. I employed Parallax Propeller chip to build a serial terminal device for this computer. My requirements were as follows:
* Dumb terminal with some subset of ANSI functionality with a keyboard (PC or self-made) and at least 80 columns x 25 rows display.
* Mass storage (SD card) that would allow to transfer files to host computer (not PC, the MKHBC-8-R2) and save output from computer coming via serial port to a file on SD card.
This serial terminal device is coming together nicely. It is not the last word though. In the final version I would like to have a Propeller based I/O card which serves keyboard input and display output, maybe some sound and joystick / paddle ports as well which interfaces with MKHBC-8-R2 via CPU or I/O bus instead of slow serial port. This serial terminal is sort of an intermediate step allowing me to get more familiar with Propeller chip.
So far I was able to put together hardware and code to:
* Connect to MKHBC-8-R2 via serial (RS-232) with 9600 baud speed.
* Display hi-res text on VGA display.
* Interface my home-brew retro computer keyboard (TI99/4A + 8052 micro as controller, serial interface similar to PS2) to Propeller and write my driver code. I also have a variant for PC keyboard, both presenting identical API interface, so all I need to do is load different driver to cog if I want to use a different keyboard, other code does not change.
* Interface micro SD card to the Propeller and write Terminal Menu code which aids in sending contents of the text file from SD card to the serial port or dumping memory of MKHBC-8-R2 system to a file on SD card in the form of monitor memory write commands. Thus I can prepare file with monitor memory write commands on a PC or MKHBC system, store on SD card and then send them to the MKHBC-8-R2 system - a primitive way of of loading or saving data, but effective.
* Implemented few ANSI terminal functions, like clearing screen or positioning cursor at specified column and row.
I will soon post the code to github and update project with a link to it.
On this screenshot I just uploaded Tiny Basic from SD card, ran it, then uploaded BASIC program listing from SD card effectively entering BASIC program into the memory just as I would do it from keyboard, then listed it and ran it.
This is how the startup screen looks like. The 'mkhbc>' string is a prompt from the MKHBC-8-R2 computer received via serial port.
My development system. I am using Quick Start board from Parallax with Human Interface shield on top for VGA and keyboard interfacing. To the right you see TI99/4A keyboard with 8052 controller (not visible, tucked in under the keyboard) connected to the Human Interface board. PC is the host for SPIN program development. The serial port interface is put together on the breadboard on the left of the Parallax board (just 3 resistors). The VGA monitor connected to the Human Interface shield is the one on the left that you can only see a corner of with the green glow.
This is the Parallax Propeller USB Project board. This board will ultimately host the serial terminal device. I don't use it for code development for the serial terminal project because of the problem with powering keyboard from the USB port. To test code I needed to disconnect the USB programming cable and connect external power supply. For this reason I had to write the program to EPROM each time I made new build iteration if I needed to test it with the keyboard. Therefore I was afraid I was going to wear down the EPROM prematurely by frequent writing and brick the project board.
On the project area I added serial interface, 5V power supply, keyboard mini-din socket, various headers and connections to VGA and micro SD which has a socket on the bottom side of the board. I may also add some sound generating hardware there (probably some simple stuff, few resistors and socket as the actual sound will be generated by software in the Propeller).
The actual MKHBC-8-R2 computer as it looks now. CPU/ROM/RAM card is on the left, UART to the right and RTC and banked RAM is still on the breadboard.
Keyboard interface is the same as on Parallax Human Interface Board. On my project board I just assembled the keyboard part, leaving the mouse connector off.
This is the serial interface circuit. Pretty simple and it works:
Code (not all, just main cog):{{ Homebrew Serial Keyboard + VGA Terminal (80x40) and SD card. Marek Karcz (C) 2016, 2017. All rights reserved. Free for personal and educational use. Serial keyboard consists of matrix retro keyboard (TI 99/4A) and AT89S52 controller + open collector clock and data output, pretty much like a PS/2 keyboard, but the protocol is different. See documentation in serkb_recv object. This configuration uses P8X32A QuickStart Board + Human Interface Board from Parallax INC. Terminal will also work with PC keyboard and Keyboard object. }} CON _clkmode = xtal1 + pll16x 'Use low crystal gain, wind up 16x _xinfreq = 5_000_000 'External 5 MHz crystal on XI & XO ''_CLKFREQ = 80_000_000 SDA_pin = 26 'keyboard port on human interface board SCL_pin = 27 'SDA_pin = 24 'keyboard port on human interface board 'SCL_pin = 25 SerialTx_pin = 5 SerialRx_pin = 7 SerialBaud = 9600 SerialMode = %0011 MAX_col = scr#cols - 1 MAX_row = scr#rows - 1 chrs = scr#cols * scr#rows 'MAX_col = 31 'MAX_row = 14 CRSBLDEL = 3000 BKSPC = 8 BKSPC_PC = $C8 CTRL_H_PC = $268 NUMLOCK_PC = $DF NL = $0D CR = $0A CTRL_C = 3 ' CTRL-C from TI99/4A keyboard driver CTRL_C_PC = $263 ' CTRL-C from PS/2 keyboard driver CTRL_Z = 26 CTRL_Z_PC = $27A SPC = $20 ESC_PC = $CB ESC = $1B F1_PC = $D0 CTRL_Q_PC = $271 CTRL_Q = $11 ' Micro SD connections CS = 3 ' Propeller Pin 3 - Set up these pins to match the Parallax Micro SD Card adapter connections. DI = 2 ' Propeller Pin 2 - For additional information, download and refer to the Parallax PDF file for the Micro SD Adapter. CLK = 1 ' Propeller Pin 1 - The pins shown here are the correct pin numbers for my Micro SD Card adapter from Parallax D0 = 0 ' Propeller Pin 0 - In addition to these pins, make the power connections as shown in the following comment block. OBJ scr : "vga_hires_text_mk" serkb : "serkb_recv" ' serkb_recv object is interchangeable 'serkb : "keyboard" ' with keyboard object - no more code ' changes are required, just swap them here rs232 : "FullDuplexSerial_mk" 'serial : "Parallax Serial Terminal" 'Num : "numbers" sdfat : "fsrw" ' r/w file system streng : "ASCII0_STREngine_1" ' string library DAT str01 BYTE "Serial Keyboard + VGA Terminal (80x40).",NL,0 strFmVer BYTE "Firmware version 2.0.",NL,0 strCpr01 BYTE "Copyright (C) by Marek Karcz 2016,2017.",NL,0 strCpr02 BYTE "All rights reserved.",NL,0 str01_1 BYTE NL,"Press (at any time):",NL,NL,0 str02 BYTE " CTRL-C to Clear Screen,",NL,0 str03 BYTE " CTRL-H to Backspace/Delete,",NL,0 str04 BYTE " CTRL-Z to open Terminal Menu,",NL,0 str05 BYTE " CTRL-Q (F1) to see this help.",NL,0 strSdFnd BYTE NL,"SD card found. Open Terminal Menu to mount.",NL,0 strNoSd BYTE NL,"ERROR: There is no SD card.",NL,0 strSpaces BYTE " ",0 rdcmd BYTE "r ",0 VAR long col, row long rcv, key, prevkey 'long crsct 'sync long - written to -1 by VGA driver after each screen refresh long sync 'screen buffer - could be bytes, but longs allow more efficient scrolling long screen[chrs/4] 'row colors word colors[MAX_row+1] 'cursor control bytes byte cx0, cy0, cm0, cx1, cy1, cm1 byte fname[13] byte staddr[5], endaddr[5] byte memrdcmd[16], buf[80] long sdcard_found PUB Main | i sdcard_found := -1 prevkey := 0 'crsct := CRSBLDEL col := 0 row := 0 'Num.init 'serial.Start(115200) rs232.Start(SerialRx_pin, SerialTx_pin, SerialMode, SerialBaud) serkb.Start(SDA_pin, SCL_pin) ' Start the serial keyboard object scr.start(16, @screen, @colors, @cx0, @sync) 'set up colors, clear screen repeat i from 0 to MAX_row colors[i] := %%0100_1310 repeat i from 0 to chrs - 1 screen.byte[i] := $20 ScrStr(@str01) ScrStr(@strFmVer) ScrStr(@strCpr01) ScrStr(@strCpr02) HelpInfo waitcnt(cnt + clkfreq) MountSD(FALSE) 'sdcard_found := \sdfat.mount_explicit(D0, CLK, DI, CS) ' Here we call the 'mount' method using the 4 pins described in the 'CON' section. if sdcard_found => 0 ScrStr(@strSdFnd) UnmountSD(FALSE) rs232.RxFlush repeat key := serkb.Key ' conversions related to used driver (ti99/4a a.k.a. serkb_recv or ps/2 a.k.a. keyboard) if key > 0 if key == CR key := NL if key == CTRL_C_PC key := CTRL_C if key == CTRL_Z_PC key := CTRL_Z if key == BKSPC_PC or key == CTRL_H_PC key := BKSPC if key == NUMLOCK_PC key := 0 if key == ESC_PC key := ESC if key == CTRL_Q_PC or key == F1_PC key := CTRL_Q if key > 0 rs232.Tx(key & $ff) 'rcv := rs232.RxCheck rcv := rs232.RxTime(20) if rcv => 0 'serial.Str(STRING("Received character from RS232:")) 'serial.Dec(rcv) 'serial.NewLine PrnChar(rcv & $ff) prevkey := rcv & $ff Cursor PUB UnmountSD(verbose) if verbose == TRUE PrnChar(NL) if sdcard_found => 0 sdfat.unmount sdcard_found := -1 if verbose == TRUE ScrStr(String("SD card has been unmounted successfully.",NL)) else if verbose == TRUE ScrStr(String("ERROR: Nothing to unmount. (already unmounted?)",NL)) PUB MountSD(verbose) if verbose == TRUE PrnChar(NL) if sdcard_found < 0 sdcard_found := \sdfat.mount_explicit(D0, CLK, DI, CS) ' Here we call the 'mount' method using the 4 pins described in the 'CON' section. if verbose == TRUE if sdcard_found => 0 ScrStr(String("SD card has been mounted successfully.",NL)) else ScrStr(String("ERROR: Unable to mount SD card, error code=")) ScrStr(streng.integerToHexadecimal(sdcard_found, 8)) PrnChar(NL) else if verbose == TRUE ScrStr(String("ERROR: Nothing to mount. (already mounted?)",NL)) PUB HelpInfo StrOut(@str01_1) StrOut(@str02) StrOut(@str03) StrOut(@str04) StrOut(@str05) Cursor PUB ScrStr(strptr) repeat StrSize(strptr) PrnChar(byte[strptr++]) PUB ScrOut(c) screen.byte[row*(MAX_col+1) + col] := c PUB StrOut(strptr) repeat StrSize(strptr) if byte[strptr] == NL col := 0 IncRow strptr++ else ScrOut(byte[strptr++]) IncCol PUB Cursor cx0 := col cy0 := row cm0 := %010 ' uncomment code below to enable own cursor implementation ' and comment code above {{ crsct-- if crsct > CRSBLDEL / 2 ScrOut("|") else ScrOut("_") if crsct == 0 crsct := CRSBLDEL }} PUB IncRow row := row + 1 if row > MAX_row row := MAX_row ByteMove(@screen, @screen+MAX_col+1, chrs-MAX_col-1) ByteFill(@screen+chrs-MAX_col-1, 32, MAX_col+1) PUB IncCol col := col + 1 if col > MAX_col col := 0 IncRow PUB DecCol if col > 0 col := col - 1 PUB ReadSerialAndPrint(rdw) repeat 'rcv := rs232.RxCheck rcv := rs232.RxTime(rdw) if rcv < 0 Quit PrnChar(rcv & $ff) PUB SDDir | n if sdcard_found < 0 ScrStr(@strNoSd) return PrnChar(NL) PrnChar(NL) ScrStr(String("Directory:",NL)) PrnChar(NL) sdfat.opendir repeat n := sdfat.nextfile(@fname) if n < 0 Quit ScrStr(@fname) PrnChar(NL) PUB GetStr(pstr, size) | n, q n := 0 q := FALSE repeat until q == TRUE key := serkb.Key if key > 0 if key == CR key := NL if key == CTRL_C_PC key := CTRL_C if key == CTRL_Z_PC key := CTRL_Z if key == BKSPC_PC key := BKSPC if key == NUMLOCK_PC key := 0 case key NL: byte[pstr+n] := 0 q := TRUE BKSPC: if n > 0 n := n - 1 byte[pstr+n] := 0 OTHER: if key > 0 and n < size-1 byte[pstr+n] := key n := n + 1 byte[pstr+n] := 0 col := 0 ScrStr(@strSpaces) ScrOut(SPC) col := 0 ScrStr(pstr) Cursor PUB EnterFileName '| n, q PrnChar(NL) ScrStr(String("Enter file name:",NL)) PrnChar(NL) Cursor GetStr(@fname, 13) PUB SendFileFromSD2Serial | n, q if sdcard_found < 0 ScrStr(@strNoSd) return SDDir EnterFileName PrnChar(NL) ReadSerialAndPrint(0) ScrStr(String("*** Loading file ***", NL)) waitcnt(cnt + clkfreq) sdfat.popen(@fname, "r") key := NL rs232.Tx(key & $ff) ReadSerialAndPrint(0) repeat key := sdfat.pgetc if key < 0 Quit if key == NL key := 0 if key == CR key := NL if key > 0 rs232.Tx(key & $ff) waitcnt(cnt + clkfreq/250) if key == NL waitcnt(cnt + clkfreq/6) ReadSerialAndPrint(0) ReadSerialAndPrint(0) PrnChar(NL) sdfat.pclose PUB SaveMemory2FileSD | m, adrbeg, adrend, curaddr if sdcard_found < 0 ScrStr(@strNoSd) return EnterFileName PrnChar(NL) ScrStr(String("Enter start address (hex DDDD, e.g: 0400):",NL)) PrnChar(NL) Cursor GetStr(@staddr, 5) adrbeg := streng.hexadecimalToInteger(@staddr) PrnChar(NL) ScrStr(String("Enter end address (hex DDDD, e.g: 1000):",NL)) PrnChar(NL) Cursor GetStr(@endaddr, 5) adrend := streng.hexadecimalToInteger(@endaddr) PrnChar(NL) Cursor if adrend - adrbeg < 15 ScrStr(String("Address range must be greater than 14 bytes.",NL)) return curaddr := adrbeg sdfat.popen(@fname, "w") repeat memrdcmd[0] := 0 streng.stringCopy(@memrdcmd, @rdcmd) streng.stringConcatenate(@memrdcmd, @staddr) streng.stringConcatenate(@memrdcmd, String("-")) curaddr := curaddr + 16 streng.stringConcatenate(@memrdcmd, streng.integerToHexadecimal(curaddr-1, 4)) streng.stringConcatenate(@memrdcmd, String(" ",NL)) ' Send the memory read command streng.stringToLowerCase(@memrdcmd) ScrStr(String("Command: ")) ScrStr(@memrdcmd) rs232.str(@memrdcmd) ' Read response from memory read command and save it to file ' line by line m := 0 repeat rcv := rs232.RxTime(20) if rcv < 0 Quit if rcv <> NL buf[m++] := rcv if m > 79 m := 0 buf[m] := 0 else buf[m++] := NL if m > 79 m := 0 buf[m++] := CR if m > 79 m := 0 buf[m] := 0 if buf[0] == "w" sdfat.pputs(@buf) ScrStr(String("buf=")) ScrStr(@buf) m := 0 ' End of address range if curaddr > adrend Quit streng.stringCopy(@staddr, streng.integerToHexadecimal(curaddr, 4)) sdfat.pclose PrnChar(NL) ScrStr(String("File saved.",NL)) PUB ListFileSD if sdcard_found < 0 ScrStr(@strNoSd) return EnterFileName PrnChar(NL) sdfat.popen(@fname, "r") repeat rcv := sdfat.pgetc if rcv < 0 Quit PrnChar(rcv & $ff) sdfat.pclose PUB TermMenu | q q := FALSE MountSD(TRUE) ' SD card us mounted only while in Terminal Menu repeat until q == TRUE ScrStr(String("Terminal Menu:",NL)) PrnChar(NL) ScrStr(String(" 1 - Send file from SD card to serial port.",NL)) ScrStr(String(" 2 - Save memory write commands to file on SD card.",NL)) ScrStr(String(" 3 - SD card directory.",NL)) ScrStr(String(" 4 - List file contents.",NL)) ScrStr(String(" 5 - Unmount SD card.",NL)) ScrStr(String(" 6 - Mount SD card.",NL)) ScrStr(String(" Q - Exit Menu",NL)) PrnChar(NL) ScrStr(String("Your selection ? ")) repeat key := serkb.Key if key > 0 case key "1" : SendFileFromSD2Serial q := TRUE "2" : SaveMemory2FileSD q := TRUE "3" : SDDir "4" : ListFileSD "5" : UnmountSD(TRUE) "6" : MountSD(TRUE) "q" : ScrStr(String("Quit.",NL)) q := TRUE OTHER: ScrStr(String("Unknown menu option.",NL)) PrnChar(NL) Quit Cursor ScrOut(SPC) UnmountSD(TRUE) ' SD card is only mounted while in Terminal Menu PUB ClrScr ByteFill(@screen, SPC, chrs) col := 0 row := 0 PUB IsDigit(c) if c => "0" and c =< "9" return TRUE return FALSE ' Read digits from rs232, convert to number and return value. ' Th las read non-digit character code is remembered in key variable. ' PUB RdNumSer(chars) : numval | c, ba numval := -1 ba := chars byte[chars] := 0 c := rs232.RxTime(20) if IsDigit(c) == TRUE repeat while IsDigit(c) == TRUE byte[chars++] := c byte[chars] := 0 c := rs232.RxTime(20) key := c numval := streng.decimalToInteger(ba) PUB PrnChar(c) | n, lcol, lrow if c == "[" and prevkey == ESC lrow := RdNumSer(@buf) if lrow => 0 case key "J" : case lrow 0 : ' write code here to clear from cursor to end of screen return 1 : ' write code here to clear from cursor to begin of screen return 2 : 'StrOut(String("[CLS]")) ClrScr return OTHER : return ";" : 'StrOut(String("[;]")) lcol := RdNumSer(@buf) if lcol => 0 if key == "H" or key == "f" col := lcol - 1 row := lrow - 1 if col < 0 col := 0 if row < 0 row := 0 return OTHER : return if c == NL 'ScrOut(SPC) 'uncomment only if own cursor impl. is used col := 0 IncRow if c == CTRL_C 'CTRL-C, Clear Screen rs232.Tx(BKSPC) 'Send backspace to delete control character ClrScr rs232.Tx(NL) 'Send NL to serial if c == CTRL_Q HelpInfo rs232.Tx(NL) 'Send NL to serial if c == BKSPC ScrOut(SPC) DecCol if c == CTRL_Z 'CTRL-Z, Menu rs232.Tx(BKSPC) 'Send backspace to delete control character ScrOut(SPC) col := 0 IncRow IncRow TermMenu IncRow rs232.Tx(NL) 'Send NL to serial if c > 31 and c < 127 ScrOut(c) IncCol {{ if c == ESC StrOut(String("[ESC]")) }} Cursor