Tuesday, January 31, 2012

MKHBC-8-R1 and RTC DS-1685 or interfacing mux-bus intel architecture chip to 6502.

I still can not overcome my laziness and start to assemble my messy bread board creation on some prototyping boards. Instead, I wanted a real time clock interfaced to my home brew computer so I could have some new hardware to write driver software for. Some time ago I bought this RTC chip from Maxim: DS1685. I didn't think at the time of any particular use for it. Just wanted to have such chip for my 8051 projects in case I needed one, in my drawer. This particular chip has bus interface compatible with multiplexed address/data bus, just like Intel/8051 architecture.  This fact posed a little bit of an issue with this project, because I wanted to connect this chip to my buffered I/O bus of MKHBC-8-R1, a separate address/data lines bus (non-multiplexed). After several failed schematics drawings, which involved using latches and sequential logic to generate appropriate signals with proper timing to strobe ALE pin and latch the address/data from 6502 non-multiplexed bus in a manner that could be understood by DS1685, I decided that there must be some easier way. Short session with Google and I came across Maxim's application note 326 for chips DS-2141A, DS-2143 and such. It presented the interface so simple, I could not believe it. It required some extra programming effort, since addressing the registers of the chip with this solution would require sending registers addresses as a regular data to I/O port. Nevertheless, it was much simpler, than doing full blown transparent hardware interface. It was too good to be true, though. It did not work. Intel-like chips do not suffer 6502 bus in some cases. I got the circuit assembled on a bread board (of course :-)) next to my LCD interface and got some test program done quickly to see if this would work. Unfortunately I could not get any data in or out of the chip. After thorough analysis of my code I decided it was OK, it must have been the hardware. My guess was, the timing was not right for reads and writes, although simple 3-nand gate glue logic worked OK for generating strobe signal for ALE pin of DS1685. After hours of tinkering, slowing down CPU clock, assembling ad hoc glue logic variants it "suddenly" hit me: it was Intel vs. 6502 bus - it did not like Phi2 synchronized signals! You see, I had my /WE and /OE Phi2 synchronized signals connected to /WR and /RD signals of DS1685. This did not work. Data were not there at the time the read/write signals were toggled. I quickly modified my circuit to use 6502's R/W signal directly for /WR of DS1685 and negated for /RD and it worked! Here is the modified interface:


Figure 1: Interfacing 6502/MKHBC-8-R1 buffered I/O bus to multiplexed bus type chip DS1685. Signal IO4 is an active low chip select signal from my I/O address decoder.

Glue logic can be done with just one 74LS00. My messy board uses extra inverter from 74LS04 because one 7400 gate is already taken by LCD interface on the same bread board (inverting of /CS signal, LCD has E-pin active HI).
With above solution, accessing registers of the RTC is done in two steps:
1) Sending register address (8-bit value) to I/O 4 port (odd address bytes).
2) Sending or reading data to/from I/O 4 port (even address bytes).

Example:

IOBase = $c000
IO4 = IOBase+4*256


DSCALADDR    =    IO4+1
DSCALDATA    =    IO4

;-------------------------------------------------------------------------------
; Write DS1685 address (Acc).
;-------------------------------------------------------------------------------


WrRTCAddr:
    sta DSCALADDR
    rts




;-------------------------------------------------------------------------------
; Write DS1685 data (Acc).
;-------------------------------------------------------------------------------


WrRTCData:
    sta DSCALDATA
    rts


;-------------------------------------------------------------------------------
; Read DS1685 data (-> Acc).
;-------------------------------------------------------------------------------


RdRTCData:
    lda DSCALDATA
    rts


;-------------------------------------------------------------------------------
; Write DS1685 Acc = Addr, X = Data
;-------------------------------------------------------------------------------


WrRTC:
    jsr WrRTCAddr
    txa
    jsr WrRTCData
    rts


;-------------------------------------------------------------------------------
; Read DS1685 Acc = Addr -> A = Data
;-------------------------------------------------------------------------------


RdRTC:
    jsr WrRTCAddr
    jsr RdRTCData
    rts

Some pictures of prototype:


Figure 2: RTC and LCD interfaces on bread board connected to buffered I/O bus. Program runs displaying seconds, minutes and hours read from RTC (hexadecimal for now) in endless loop.

Figure 3: Main board (left), buffered I/O (center) and UART (right).


I admit, I am not religious about quality of my connections. I should be more neat, even with prototyping. I get pretty anxious to get things done quickly when I get some idea and this is where the mess happens :-) . I am surprised this thing even works. It is enough to touch the wires in the wrong area to have all sorts of issues with serial communication. This thing is very sensitive. Currently I supply power to the boards from my old ATX power supply and the 7805 with big heat sink you see on the screen is not in use. It was getting awfully hot while working. I believe this is normal since the whole computer does not consume more than half amp., probably less (I need to take a measure when the thing is built on boards). But for now I just do not want any fire resulting from my hobby.

That's it for today. I have CF card interface in mind now. Just after I finish driver for this calendar/clock chip and have some chips assembled on real boards hopefully soon.



Wednesday, January 18, 2012

MKHBC-8-R1: Getting ready for some serious prototyping.


I am out of bread board grade wires and out of the desk space. It is time to move the computer's circuits gradually into the more permanent (although also prototyping) boards.
I will use the biggest prototyping board that I have to mount CPU bus and I/O expansion sockets. CPU bus will consist of 3 euro type 64-pin sockets I got from Digi-Key, P/N: 5650458-5-ND. These are vertical reception sockets and they will go to mobo and angled  connector type matching plugs (P/N: A1255-ND) will be attached to expansion cards with CPU, memory, GPU circuits and will be plugged to reception sockets in working unit. Mounted permanently on mobo will be also interrupt controller and I/O expansion bus buffer circuits plus some helper circuits (5V regulator, some signal lights, reset and NMI buttons, power switch etc.). The I/O expansion bus sockets will be made out of 40-pin IDE type male connectors. This way, I will be able to use common IDE ribbon cable to connect any prototype circuits not yet mounted permanently on their expansion cards (bread board stage of prototyping/design of I/O cards). The I/O expansion cards will have the female 40-pin connectors matching IDE male plugs in the mobo.

Figure 1: Basic idea how MKHBC-8-R1 will look like on the permanent board. 3 CPU bus expansion cards visible in front. I/O expansion bus IDE type sockets and I/O card now just put loosely over the 40-pin IDE female connector to give me the idea of the future layout.

Figure 2: View from the side. Between 64-pin sockets and IDE connectors I left a little bit of space for I/O expansion bus buffer circuit (which will simply consist of 3 x 74LS245 chips.).

Figure 3: 40-pin IDE female connector will be soldered into prototyping board and with circuit on it will become I/O expansion card. The first one to build will be of course 6850 UART.

Figure 4: CPU bus expansion card. I have 3 of these. With these I will build CPU card, memory card and GPU card.

Figure 5: Prototyping boards are ready. Time to heat up the soldering iron.

Few comments regarding this layout and why most of the circuits will be in the form of expansion cards. Well, first of all, it promotes modular architecture and I like that. It will allow me to gradually build the computer circuits and test them separately. It saves space. Also, when I design the mobo smartly, I can use it for other similar projects. Simply unplug existing cards and design new ones. Of course there will be limitations as of how the mobo can be used with different projects. E.g: it will have 16-address lines (non-multiplexed)/8-data lines bus, limiting the type of CPU architecture that can be used. Still, better this than nothing. I may one day decide to build 6809 based CPU card. I think that I mentioned in one of the prior posts that I have one because I originally wanted to build 6809 based computer inspired by Matt Sarnoff project - out of impulse I ordered parts for clock and CPU circuitry for 6809 based computer. 
The CPU card will consist of CPU clock circuit, Rockwell 6502 CPU, start up reset circuit, main I/O selection signal, I/O decoder and EPROM addresses decoding circuits. It will also have EPROM chips on board with OS/firmware. I thought of it to be logical since CPU and its firmware (like where the start up vectors are located in address space) are bound to each other. The RAM card will consist of base RAM 32 kB chip with address decoding circuit, the 128 kB RAM chip with bank switching register and address decoder (bank switched RAM will occupy 16 kB of address space from $8000 to $BFFF) and optional switch to change RAM map configuration in case GPU card will be plugged in the system (in this case the base RAM will be limited to $0000 - $5FFF instead of $0000 - $7FFF, allowing the video memory to be decoded in space $6000 - $7FFF - 8 kB, which is how much Motorola 6847 video chip can address). GPU card will consist of 6847 chip, 8 kB video RAM, video RAM address decoder circuit, buffers and glue logic allowing to share RAM access with CPU using opposite phase method, composite video amplifying transistors/color mixing circuit and I/O write only register for setting the video mode of the 6847 chip. I am not yet certain about what I will use as video mode register. I have been thinking about 6522, but this seems to be overkill. I want to save 6522 for my interrupt timer and some I/O circuitry for future projects like IDE interface and such. So, I will probably opt for something simple, like the chip I used for RAM banking register (see schematics, 74LS374 3-state edge triggered octal flip-flops). The GPU card I save for later. I consider it the difficult part of the project. Before that I will build 6850 UART card (will go into I/O expansion bus), which is logical because I want to have some means of communicating with my system via terminal emulator. I also want to build real-time keeping card. I bought DS1685 chip some time ago not thinking about any particular project that I will use this chip with. Now I face the dilemma of attaching the multiplexed address bus type chip to non-multiplexed 6502 bus. I have some ideas, but not very clear yet.
So much to do and fun ahead, but so little free time.

Thanks for reading.

---
Marek

Tuesday, January 17, 2012

MKHBC-8-R1: testing buffered I/O expansion bus - interfacing to LCD text module.

Today I built the simplest possible peripheral device by attaching LCD text display module to my system. It is a simple 16x2 LCD based on HD44780 chip, the most common one. I got it with my 8051 development board. Quick assembly on the bread board and had it running in no time. I didn't even need to write any software to test it (it is that simple). The device's Enable (E) pin is connected to my I/O address decoder output /IO3 (base address $c300) via inverting gate (E pin is active high). After I executed following sentence of data writes in monitor:

Figure 1: Writing bytes to LCD registers.

which consists of 4 bytes initialization sequence 38,0c,06,01 (01 is a clear screen code) and ASCII codes of sequence "ABC012" I got following results:

Figure 2: LCD seems to work (and so my buffered I/O expansion bus).

Here is the electrical diagram:

Figure 3: LCD interfacing.

Figure 4: MKHBC-8-R1 setup with main CPU board, I/O expansion buffer, UART and LCD.

Thus I confirmed that both my I/O address decoder and buffered I/O expansion bus work by having 2 peripheral devices connected to the I/O bus and working without any conflicts.
The whole thing becomes a monster of bread boards and wires. In fact I start to run out of both. I think it is time to start assembling this thing on more permanent prototyping boards. I will use point-to-point soldering method. It is a lot of work, but I have lots of magnet wire, solder and prototyping boards.

Done for today. Thank you for reading.

---
Marek


Sunday, January 15, 2012

MKHBC-8-R1: Buffered I/O expansion bus.

I decided to make a bit of progress with hardware prototyping. Specifically I wanted to add more I/O devices to my computer (real time clock, 6522 ports) and have a bit of fun coding drivers for them. But for that I need buffered I/O expansion bus.
In my first post about MKHBC-8-R1 I mentioned that I would consider two architectures. One with passive motherboard and expansion sockets, to which self address-selected and self buffered cards would be plugged and the other with CPU and address selection circuits on mobo and buffered I/O expansion bus. Today I decided that I will go with active motherboard with clocks, CPU, IRQ controller, address and I/O decoding and two expansion buses - one for memory/video cards (non-buffered CPU bus with 2 or 3 euro bus type sockets) and second bus for I/O devices like data input/output ports, serial/parallel ports, perhaps a sound card  (it is going to be a buffered bus with 6 expansion sockets, I will probably use IDE connectors, one set of bi-directional buffers made with 74LS245 chips, with total of 24 lines: 8 address lines A0-A7, 8 data lines and some  control signals). Each of the I/O expansion sockets will have one pin connected to one of the I/O selection lines from I/O address decoder (so each will have its own 256 bytes long address space) and one pin connected to one of the inputs of interrupt controller. For interrupt controller I decided to use circuit proposed by Paul Fellingham. It is simple and elegant. I just needed to add some glue logic to incorporate it into my design (the interrupt controller has one read and one write port so it is I/O device which I need to put in dedicated address space).
I do not expect memory or graphics card to be an interrupt source, so the CPU non-buffered bus will not have interrupt pins assigned, however it will have full address bus and all CPU control signals on it. I also buffered phase 2 signal from CPU (2 inverters). I built the I/O expansion bus buffer circuit on a bread board and attached my only (for now) I/O device (6850 based UART/serial port) to it (it was previously on CPU bus). It seems to be working fine. I am able to communicate with my computer over serial connection, send and receive data etc. The real test (and fun) will commence when I build the interrupt controller and attach more devices to the I/O expansion bus. Then I will see if there are no conflicts on the bus and timing issues and such. I updated my schematics which are available online in PDF format (sorry, they are still hand drawings, one day I will draw them in some circuit drawing program, for now this will have to suffice).


Figure 1: The lower small bread board is an I/O expansion bus buffer circuit.
The upper board is 6850 UART connected to the I/O Expansion Bus.
At the top of picture is a MAX 232 module board I got on e-bay and serial to USB converter (also from e-bay) which allows to connect MKHBC to my PC's USB port.

Figure 2: The whole MKHBC-8-R1 prototype.
CPU sits in right lower corner on the big bread board.
EPROM chips are located mid-upper left below LEDs.
RAM (32kB) is located to the right side of EPROMs (above it is 7805 5V regulator).
Just a bit to the left of RAM chip is I/O decoder circuit.
Below EPROMS is main I/O selection and EPROM selection address decoding circuit.
To the left of CPU are clock  and reset circuits and some glue logic.

The whole prototype consists of 16 ICs now (not counting can oscillators, which are 2, one for CPU and future video card and one for Tx, Rx speed of 6850 uart). Not that many, considering this is going to be general purpose computer system, however there will be much more (probably double current amount) with all the expansion cards that I plan to build. I probably need to consider using more capable power supply (currently I just use universal 1500 mA wall socket power adapter and single 7805 5V regulator. It (7805 with small radiator) gets pretty hot while working (but not sizzling yet).

That's all for today, folks! Thanks for reading.

Marek


Saturday, January 14, 2012

Porting Peter Jennings's Microchess to MKHBC-8-R1.

While the concept of my home brew computer was still in the abstract phase, I have been doing lots of research on the internet regarding the topic of building ones own 8-bit computer system. I found lots of cool web sites and great home brew projects. Some of them referenced Peter Jennings's "Microchess" chess implementation running on their hardware (e.g: Big Mess of Wires or BMOW project). I thought it would be awesome to have this software running on my platform. Through this great portal, I found reference to source code of the implementation of "Microchess" modified by Daryl Rictor to have the program communicate with terminal via serial port (original implementation was on KIM-1 using its hex keypad and 7-segment display).I only modified some zero page addresses so the program would not interfere with my OS and supplied my own I/O functions that called MKHBCOS API and preserved A and X registers. Easy as pie, but... it did not work.
After hours of debugging and looking at the code I finally realized (again) that cl65 has put one segment of my code at wrong address due to incorrect configuration.
Here is the configuration file for my port of "Microchess":

MEMORY {
    RAM1:    start = $0400, size = $0520, fill = yes;
    RAM2:    start = $0920, size = $006E;
}

SEGMENTS {
    CODE:    load = RAM1,    type = rw;
    DATA:    load = RAM2,    type = rw;
}

Before the corrections, the size property of RAM1 was one byte shorter ($051F). Notice fill=yes property setup? That's right, as you probably already realized, my DATA segment was put at wrong address because of the wrong size declaration of RAM1 memory. When using fill=yes property, (which is necessary to produce continuous code for my bin2hex tool that generates write memory statements to plain text file, which are then sent to the MKHBC-8-R1 via terminal emulator using Send Text File feature), the size of the memory section must be the exact difference between the start of the next section and start of the current section. In above example: $0920 - $0400 = $0520.
After correction to configuration file cl65 linked all segments of program to proper address locations and runs flawlessly:


Figure 1: Peter Jennings's "Microchess" running on MKHBC-8-R1.

Next step (if I ever find time) is to improve the user interface a bit. The presentation is OK I guess, It wont get much better in text mode. However data input is a bit awkward (commands are taken key by key, refreshing the screen unnecessarily after each key stroke, instead of just taking full command line and parsing it when fully entered). I think this is KIM-1's legacy of taking input from hex keypad, Daryl Rictor did not change this part of code much). Well, poor user friendliness is the price you pay for compact code. The whole thing fits in just 1407 bytes! Original program on KIM-1 ran in standard 1 kB of RAM. The whole chess engine! This is awesome. As a professional programmer I can appreciate efficiency in coding.

That's it for today. Thank you for reading.

Marek

Wednesday, January 11, 2012

Coding in 'C' with cc65 for custom 6502 platform.

Hello folks!

In this article just few remarks or should I say errata to previously published article about coding in 'C' and compiling/linking programs with cc65/cl65 to be loaded and ran from RAM on my MKHBC-8-R1 computer:

1) The memory configuration previously published in my blog has a flaw resulting in program not being properly linked (basically linker restarted ORG address several times to be at the beginning of my defined RAM memory). Some code configurations worked, however I noticed that with array (or any other variables) placed at the global section past constant data initialization, resulting code did not work. I went to debugging and realized that the linked code is all at wrong addresses.
Apart from ZP (zero page) section, separate non-overlapping RAM address ranges must be designated for DATA/BSS/HEAP, STARTUP/INIT/CODE and RODATA. Here is new config file mkhbcoslib.cfg:

MEMORY {
    ZP:     start    =   $20,     size =  $E0,    type   = rw, define = yes;
    RAMC:   start    =   $0400,   size =  $0400,  fill = yes;
    RAMD:   start    =   $0800,   size =  $0400,  fill = yes;
    RAM:    start    =   $0C00,   size =  $0400,  define = yes;
}

SEGMENTS {
    ZEROPAGE:  load = ZP,  type = zp,  define   = yes;
    DATA:      load = RAM, type = rw,  define   = yes;
    BSS:       load = RAM, type = bss, define   = yes;
    HEAP:      load = RAM, type = bss, optional = yes;
    STARTUP:   load = RAMC, type = rw;
    INIT:      load = RAMC, type = rw,  optional = yes;
    CODE:      load = RAMC, type = rw;
    RODATA:    load = RAMD, type = rw;
}

FEATURES {
    CONDES:    segment = STARTUP,
               type    = constructor,
               label   = __CONSTRUCTOR_TABLE__,
               count   = __CONSTRUCTOR_COUNT__;
    CONDES:    segment = STARTUP,
               type    = destructor,
               label   = __DESTRUCTOR_TABLE__,
               count   = __DESTRUCTOR_COUNT__;
}

SYMBOLS {
    # Define the stack size for the application
    __STACKSIZE__:  value = $0200, weak = yes;
}


With above configuration, mentioned sections will be consolidated with ORG addresses at adequate designated RAM sections starting addresses. Previously linker placed these segments several times into the same memory space, resulting in garbage instead of program.
Statement fill=yes is necessary for RAMC and RAMD memory sections in order for the resulting binary image to be continuous, so that my bin2hex conversion utility would produce nice loading script for hyper terminal/MKHBCOS user interface.

2) Due to above issues, the initialization routine in crt0.s - the start up code implementation for cc65 to properly run programs on custom platform, did not work. With the corrections above done, they could be un-commented and they work now. The resulting linked program starts with the STARTUP section so the load address is the execute address at the same time. Here is the corrected crt0.s code, which I had to reload to my custom cc65 library mkhbcos.lib with ar65 program (see cc65 documentation for details):

; ---------------------------------------------------------------------------
;
; File:        crt0.s
; Author:    Marek Karcz
;
; Original work: cc65 documentation/customization tutorial
;
; Purpose:
;
;     Startup code for cc65 (MKHBC-8-R1 version, to run under MKHBCOS,
;     M.O.S. derivative).
;
; Revision history:
;
; 2012-01-10:
;    Initial revision.
;
; 2012-01-11:
;   Initialization of memory storage enabled.
;
; ---------------------------------------------------------------------------

.export   _init, _exit
.import   _main

.export   __STARTUP__ : absolute = 1        ; Mark as startup
.import   __RAM_START__, __RAM_SIZE__       ; Linker generated

.import    copydata, zerobss, initlib, donelib

.include  "zeropage.inc"

; ---------------------------------------------------------------------------
; Place the startup code in a special segment

.segment  "STARTUP"

; ---------------------------------------------------------------------------
; A little light 6502 housekeeping

; This is the entry point of compiled and linked program when ran under
; M.O.S. Look in the map file for beginning of STARTUP segment.
; Provide that address to 'x' command (remember to use lower letters).

_init:   
        ; NOTE: This part is already done by MKHBCOS
          ;LDX     #$FF                 ; Initialize stack pointer to $01FF
          ;TXS
          ;CLD                          ; Clear decimal mode

; ---------------------------------------------------------------------------
; Set cc65 argument stack pointer
; Must run, otherwise stack pointer will point to $ffff.

          LDA     #<(__RAM_START__ + __RAM_SIZE__)
          STA     sp
          LDA     #>(__RAM_START__ + __RAM_SIZE__)
          STA     sp+1

; ---------------------------------------------------------------------------
; Initialize memory storage

          JSR     zerobss              ; Clear BSS segment
          JSR     copydata             ; Initialize DATA segment
          JSR     initlib              ; Run constructors

; ---------------------------------------------------------------------------
; Call main()

          JSR     _main

; ---------------------------------------------------------------------------
; Back from main (this is also the _exit entry):  force a software break

_exit:    JSR     donelib              ; Run destructors
          ;BRK
          rts


The whole program can be built with this batch script under Windows:

cl65 -t none --cpu 6502 --config mkhbcoslib.cfg -l -m hello.map hello.c mkhbcos_serialio.s mkhbcos.lib
bin2hex -f hello -o hello_prg.txt -w 1024 -x 1024 -s
pause prompt



I also extended my library by several I/O functions, which are basically my own implementation of the stdio routines, that work with serial port on my platform, using MKHBCOS routines. I mostly had to just properly handle the parameters passing and return values and call MKHBCOS API functions to perform the actual job:

;------------------------------------------------------------------
;
; File:     mkhbcos_serialio.s
; Author:    Marek Karcz
; Purpose:    Implement's serial console functions for MKHBCOS to be
;            called from C programs (cc65).
;
; Revision history:
;    2012-01-10:
;        Initial revision.

;  
;   2012-01-11:
;       Added several I/O routines.

;
;------------------------------------------------------------------

; M.O.S. API defines (kernal)

.define        mos_PromptLine    $80
.define        mos_PromptLen    $D0
.define     mos_StrPtr        $E0
.define        tmp_zpgPt        $F6
.define        mos_CallGetCh    $FFED
.define        mos_CallGets    $FFF3
.define        mos_CallPutCh    $FFF0
.define        mos_CallPuts    $FFF6

.setcpu    "6502"
.import ldaxysp,pushax,popax,pusha,popa
.import incsp2

.define        sp                $20

; code

.export _mos_puts,_puts,_putchar,_gets,_getchar,_getc,_fgetc
;,_read

.segment "CODE"

.proc _mos_puts: near

.segment "CODE"

    ldy #$01
    jsr ldaxysp
    sta mos_StrPtr
    stx mos_StrPtr+1
    jsr mos_CallPuts
    jsr incsp2
    rts

.endproc
   
.proc _puts: near

.segment "CODE"

    sta mos_StrPtr
    stx mos_StrPtr+1
    jsr mos_CallPuts
    lda #$00
    tax
    rts

.endproc

.proc _putchar: near

.segment "CODE"

    jsr mos_CallPutCh
    lda #$00
    tax
    rts

.endproc

.proc _gets: near

.segment "CODE"

    sta tmp_zpgPt
    stx tmp_zpgPt+1
    jsr mos_CallGets    ; entered string stored in mos_PromptLine

; copy string to the return pointer location

    ldy #$00
   
gets_l001:

    lda mos_PromptLine,y
    sta (tmp_zpgPt),y
    beq gets_end
    iny
    bne gets_l001

gets_end:

    lda tmp_zpgPt
    ldx tmp_zpgPt+1
    rts

.endproc

.proc _getchar: near

.segment "CODE"

    jsr mos_CallGetCh
    ldx #$00
    rts

.endproc

.proc _getc: near

.segment "CODE"

    jsr mos_CallGetCh
    ldx #$00
    rts

.endproc

.proc _fgetc: near

.segment "CODE"

    jsr mos_CallGetCh
    ldx #$00
    rts

.endproc

Added some code to hello.c to test some of the new functions:

/*
 *
 * File: hello.c
 * Purpose: Hello World program to run under MKHBCOS
 *          (M.O.S. derivative).
 * Author: Marek Karcz
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern void mos_puts(char *s);

char hello[] = "Hello World!\n\r";
char buf[256];

void my_puts (char *s)
{
    mos_puts(s);
}

int main (void)
{
    my_puts(hello);
    mos_puts("It sure feels good to code in 'C' for g'old MOS 6502!\n\r");
    puts("Enter text:");
    gets(buf);
    putchar('\n');
    putchar('\r');
    puts("You entered:");
    puts(buf);
    putchar('\n');
    putchar('\r');

    return 0;
}


The results were satisfying:



That is it for today. Thank you for reading.

M.K.

Monday, January 9, 2012

MKHBC-8-R1 homebrew 6502 based computer - coding in 'C', CC65 compiler.


Today I configured environment to compile 'C' programs for my platform. I use my favorite cc65 compiler. The documentation contains nice comprehensive tutorial how to configure compiler to target your specific 6502 based hardware platform. Using that tutorial I created my ctr0.s assembler file - to be added to custom library - that defines start up code for custom platform:


; ---------------------------------------------------------------------------
;
; File:        crt0.s
; Author:    Marek Karcz
;
; Original work: cc65 documentation/customization tutorial
;
; Purpose:
;
;     Startup code for cc65 (MKHBC-8-R1 version, to run under MKHBCOS,
;     M.O.S. derivative).
;
; Revision history:
;
; 2012-01-10:
;    Initial revision.
;
; ---------------------------------------------------------------------------

.export   _init, _exit
.import   _main

.export   __STARTUP__ : absolute = 1        ; Mark as startup
.import   __RAM_START__, __RAM_SIZE__       ; Linker generated

.import    copydata, zerobss, initlib, donelib

.include  "zeropage.inc"

; ---------------------------------------------------------------------------
; Place the startup code in a special segment

.segment  "STARTUP"

; ---------------------------------------------------------------------------
; A little light 6502 housekeeping

; This is the entry point of compiled and linked program when ran under
; M.O.S. Look in the map file for beginning of STARTUP segment.
; Provide that address to 'x' command (remember to use lower letters).

_init:    
        ; NOTE: This part is already done by MKHBCOS
          ;LDX     #$FF                 ; Initialize stack pointer to $01FF
          ;TXS
          ;CLD                          ; Clear decimal mode

; ---------------------------------------------------------------------------
; Set cc65 argument stack pointer
; Must run, otherwise stack pointer will point to $ffff.

          LDA     #<(__RAM_START__ + __RAM_SIZE__)
          STA     sp
          LDA     #>(__RAM_START__ + __RAM_SIZE__)
          STA     sp+1

; ---------------------------------------------------------------------------
; Initialize memory storage

;        NOTE: I have no idea why, but with this code enabled, programs
;                do not work under MKHBCOS.

          ;JSR     zerobss              ; Clear BSS segment
          ;JSR     copydata             ; Initialize DATA segment
          ;JSR     initlib              ; Run constructors

; ---------------------------------------------------------------------------
; Call main()

          JSR     _main

; ---------------------------------------------------------------------------
; Back from main (this is also the _exit entry):  force a software break

_exit:    ;JSR     donelib              ; Run destructors
          ;BRK
          rts


I did not figure out yet everything (see commented code and comments), however this setup works for my simple "Hello World" program:

/*
 *
 * File: hello.c
 * Purpose: Hello World program to run under MKHBCOS
 *          (M.O.S. derivative).
 * Author: Marek Karcz
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern void mos_puts(char *s);

char hello[] = "Hello World!\n\r";

void my_puts (char *s)
{
    mos_puts(s);
}

int main (void)
{
    my_puts(hello);
    mos_puts("It sure feels good to code in 'C' for g'old MOS 6502!\n\r");

    return 0;
}


...one more:

;------------------------------------------------------------------
;
; File:     puts.s
; Author:    Marek Karcz
; Purpose:    Implement's mos_puts procedure (MKHBCOS's Puts) to be
;            called from C programs (cc65).
;
; Revision history:
;    2012-01-10:
;        Initial revision.
;
;------------------------------------------------------------------

; M.O.S. API defines

.define     mos_StrPtr        $E0
.define     mos_CallPuts    $FFF6

.setcpu    "6502"
.import ldaxysp
.import incsp2

; code

.export _mos_puts

.segment "CODE"

.proc _mos_puts: near

.segment "CODE"

    ldy #$01
    jsr ldaxysp
    sta mos_StrPtr
    stx mos_StrPtr+1
    jsr mos_CallPuts
    jsr incsp2
    rts

.endproc

   
I built everything with this batch script:


cl65 -t none --cpu 6502 --config mkhbcoslib.cfg -l -m hello.map hello.c puts.s mkhbcos.lib
bin2hex -f hello -o hello_prg.txt -w 1024 -x 1039
pause prompt


...and uploaded to my computer. Moment of anxiety.... and....


Success!

That's it for today. Thank you for reading.

M.K.