WOZMON – a memory monitor in 256 bytes

The woz monitor, also known as WOZMON, is a pretty simple memory monitor and was the system software located in the 256 byte PROM on the Apple I.
Wozmon is used to inspect and modify memory contents or to execute programs already located in memory. Steve Wozniak managed to squeeze all that functionality into 256 bytes. That’s right, bytes. Not megabytes, not kilobytes. Bytes.

We already had attempted to get wozmon ported to our Steckschwein, but we did not succeed so far. That might have been because the wozmon-code is a little bit hard to read and makes use of some Apple I specific things, which we did not know they were, since we do not have any expertise about the Apple I.

Fortunately, we got asked by a friend to proof-read his upcoming in-depth article about the Apple I and wozmon, which provided us with the missing background knowledge. So, as a proof of correctness and helpfulness of his article, it was time for a new porting attempt. As it turned out, there had to be 2 bigger changes, one while a char is input, and one while a char is output, and a few smaller but important considerations.

We started off using Jeff Tranter’s Version, because Jeff saved us some grunt work by having adapted the code to ca65 syntax.

So here is our Version of wozmon adapted to run on top of SteckOS in all it’s glory:

;  The WOZ Monitor for the Apple 1
;  Written by Steve Wozniak in 1976

Credit where credit is due!

.include "common.inc"
.include "../kernel/kernel.inc"
.include "../kernel/kernel_jumptable.inc"
.include "appstart.inc"

appstart $1000

Our Standard SteckOS includes. The appstart macro takes care of creating a commodore style file “header” with the load address in the first 2 bytes of the file.

; Page 0 Variables
XAML            = $24           ;  Last "opened" location Low
XAMH            = $25           ;  Last "opened" location High
STL             = $26           ;  Store address Low
STH             = $27           ;  Store address High
L               = $28           ;  Hex value parsing Low
H               = $29           ;  Hex value parsing High
YSAV            = $2A           ;  Used to see if hex value is given
MODE            = $2B           ;  $00=XAM, $7F=STOR, $AE=BLOCK XAM

Nothing changed here.

; Other Variables
IN              = $0300         ;  Input buffer to $027F
; KBD             = $D010         ;  PIA.A keyboard input
; KBDCR           = $D011         ;  PIA.A keyboard control register
; DSP             = $D012         ;  PIA.B display output register
; DSPCR           = $D013         ;  PIA.B display control register

               ; .org $FF00
               ; .export RESET

We need to put the input buffer from $027F to somewhere else, because $027F collides with our I/O area. $0300 should be fine.
We do not use a PIA for i/o, so we can get rid of those labels. Also, the start address is already defined above, and we won’t need to export the RESET label.

RESET:          CLD             ; Clear decimal arithmetic mode.
                LDY #$7F        ; Mask for DSP data direction register.
                ; STY DSP         ; Set it up.
                LDA #$A7        ; KBD and DSP control register mask.
                ; STA KBDCR       ; Enable interrupts, set CA1, CB1, for
                ; STA DSPCR       ; positive edge sense/output mode.

No need to initialize the PIA chip which we don’t have, but we still need to initialize the A and Y registers.

NOTCR:          ; CMP #'_'    ; "_"?
                CMP #$08 + $80
                BEQ BACKSPACE   ; Yes.

Backspace is $08 on the Steckschwein, not “_”.

                CMP #$9B        ; ESC?
                BEQ ESCAPE      ; Yes.
                INY             ; Advance text index.
                BPL NEXTCHAR    ; Auto ESC if > 127.
ESCAPE:         LDA #'\' + $80   ; "\".
                JSR ECHO        ; Output it.
GETLINE:        LDA #$8A        ; CR.
                JSR ECHO        ; Output it.
                LDY #$01        ; Initialize text index.
BACKSPACE:      DEY             ; Back up text index.
                BMI GETLINE     ; Beyond start of line, reinitialize.
                ; LDA KBDCR       ; Key ready?
                ; BPL NEXTCHAR    ; Loop until ready.
                ; LDA KBD         ; Load character. B7 should be ‘1’.
                ORA #$80

Here is our first major code change. Keyboard input is handled using SteckOS means. Then we convert the received character to uppercase, since the Apple I uses uppercase only, hence wozmon does not handle lowercase.
Also, and most important, the Apple I keyboard generated ASCII with bit 7 set to “1”. We need to emulate that.
With these modifications, the bulk of the code can remain as is.

                STA IN,Y        ; Add to text buffer.
                JSR ECHO        ; Display character.
                CMP #$8D        ; CR?
                BNE NOTCR       ; No.
                LDY #$FF        ; Reset text index.
                LDA #$00        ; For XAM mode.
                TAX             ; 0->X.
SETSTOR:        ASL             ; Leaves $7B if setting STOR mode.
SETMODE:        STA MODE        ; $00=XAM $7B=STOR $AE=BLOK XAM
BLSKIP:         INY             ; Advance text index.
NEXTITEM:       LDA IN,Y        ; Get character.
                CMP #$8D        ; CR?
                BEQ GETLINE     ; Yes, done this line.
                CMP #'.' + $80    ; "."?
                BCC BLSKIP      ; Skip delimiter.
                BEQ SETMODE     ; Yes. Set STOR mode.
                CMP #':' + $80   ; ":"?
                BEQ SETSTOR     ; Yes. Set STOR mode.
                CMP #'R' + $80   ; "R"?
                BEQ RUN         ; Yes. Run user program.
                STX L           ; $00-> L.
                STX H           ; and H.
                STY YSAV        ; Save Y for comparison.
NEXTHEX:        LDA IN,Y        ; Get character for hex test.
                EOR #$B0        ; Map digits to $0-9.
                CMP #$0A        ; Digit?
                BCC DIG         ; Yes.
                ADC #$88        ; Map letter "A"-"F" to $FA-FF.
                CMP #$FA        ; Hex letter?
                BCC NOTHEX      ; No, character not hex.
DIG:            ASL
                ASL             ; Hex digit to MSD of A.
                LDX #$04        ; Shift count.
HEXSHIFT:       ASL             ; Hex digit left, MSB to carry.
                ROL L           ; Rotate into LSD.
                ROL H           ;  Rotate into MSD’s.
                DEX             ; Done 4 shifts?
                BNE HEXSHIFT    ; No, loop.
                INY             ; Advance text index.
                BNE NEXTHEX     ; Always taken. Check next char for hex.
NOTHEX:         CPY YSAV        ; Check if L, H empty (no hex digits).
                BEQ ESCAPE      ; Yes, generate ESC sequence.
                BIT MODE        ; Test MODE byte.
                BVC NOTSTOR     ;  B6=0 STOR 1 for XAM & BLOCK XAM
                LDA L           ; LSD’s of hex data.
                STA (STL,X)     ; Store at current ‘store index’.
                INC STL         ; Increment store index.
                BNE NEXTITEM    ; Get next item. (no carry).
                INC STH         ; Add carry to ‘store index’ high order.
TONEXTITEM:     JMP NEXTITEM    ; Get next command item.
RUN:            JMP (XAML)      ; Run at current XAM index.
NOTSTOR:        BMI XAMNEXT     ; B7=0 for XAM, 1 for BLOCK XAM.
LDX #$02        ; Byte count.
SETADR:         LDA L-1,X       ; Copy hex data to
                STA STL-1,X     ; ‘store index’.
                STA XAML-1,X    ; And to ‘XAM index’.
                DEX             ; Next of 2 bytes.
                BNE SETADR      ; Loop unless X=0.
NXTPRNT:        BNE PRDATA      ; NE means no address to print.
                LDA #$8A        ; CR.
                JSR ECHO        ; Output it.
                LDA XAMH        ; ‘Examine index’ high-order byte.
                JSR PRBYTE      ; Output it in hex format.
                LDA XAML        ; Low-order ‘examine index’ byte.
                JSR PRBYTE      ; Output it in hex format.
                LDA #':' + $80  ; ":".
JSR ECHO        ; Output it.
PRDATA:         LDA #$A0        ; Blank.
                JSR ECHO        ; Output it.
                LDA (XAML,X)    ; Get data byte at ‘examine index’.
                JSR PRBYTE      ; Output it in hex format.
XAMNEXT:        STX MODE        ; 0->MODE (XAM mode).
                LDA XAML
                CMP L           ; Compare ‘examine index’ to hex data.
                LDA XAMH
                SBC H
BCS TONEXTITEM  ; Not less, so no more data to output.
                INC XAML
BNE MOD8CHK     ; Increment ‘examine index’.
                INC XAMH
MOD8CHK:        LDA XAML        ; Check low-order ‘examine index’ byte
                AND #$07        ; For MOD 8=0
BPL NXTPRNT     ; Always taken.
PRBYTE:         PHA             ; Save A for LSD.
                LSR             ; MSD to LSD position.
JSR PRHEX       ; Output hex digit.
                PLA             ; Restore A.
PRHEX:          AND #$0F        ; Mask LSD for hex print.
                ORA #'0' + $80  ; Add "0".
                CMP #$BA        ; Digit?
                BCC ECHO        ; Yes, output it.
                ADC #$06        ; Add offset for letter.
                ; BIT DSP         ; bit (B7) cleared yet?
                ; BMI ECHO        ; No, wait for display.
                ; STA DSP         ; Output character. Sets DA.
                and #$7F
                jsr krn_chrout
                RTS             ; Return.

Second important change. We got rid of the Apple I specific routine to output characters and use the chrout-routine of the SteckOS-kernel. But in order not to output garbage, we need to unset bit 7. Since all comparisons afterwards still rely on bit 7 being set, we save the A register to the stack and restore it afterwards.
A faster way would be to just set bit 7 again by doing a ORA #$80 before the RTS, but what the heck.

; BRK             ; unused
; BRK             ; unused

We don’t need those.

; Interrupt Vectors
; .WORD $0F00     ; NMI
; .WORD $0000 ; BRK/IRQ

We don’t need those either since we are not using wozmon as system software. Interrupt handling is still done by the SteckOS kernel.






Posted in debugging, experiment, hexdump, software

VCFe diesmal ohne Steckschwein

Zum ersten Mal in der Geschichte des Steckschweins sind wir dieses Jahr nicht als Aussteller auf dem VCFe dabei.

Das Kommen lohnt sich aber trotzdem – diesmal findet das VCFe nämlich im Leibniz-Rechenzentrum der Bayerischen Akademie der Wissenschaften in Garching statt.


Posted in Allgemein

Switching to english

There seems to be quite some interest in our little project from outside of germany. Thanks to all who are following what we are doing.

To save you the effort of putting our blatherings through Google Translate or even having to learn german, we decided switch to writing our blog articles in english.

The rest of the site will be translated as we go. The hardware page has already been translated.


Posted in Allgemein

V9958 – Es ist vollbracht

Da ist sie nun, die neuen Videoplatine. Nach einigen Umwegen hat der Packen Boards uns erreicht (die erste Lieferung ist versehentlich in Spanien gelandet, während wir die Platinen des spanischen Bastlers erhalten haben. Inzwischen hat jeder Ersatz erhalten).

Wir haben ja bekanntlich unsere Vorgehensweise geändert, indem wir schneller Platinen anfertigen lassen. Aber zu unserer großen Freude funktionierte die erste Platine nach Bestückung auf Anhieb. Selbstverständlich haben sich aber auch ein paar Patzer eingeschlichen.


Die bis auf die Chinch-Buchsen fertig aufgebaute Platine

Zunächst gabs wohl eine Verwechslung bei den Footprints für die Chinch-Buchsen. Unsere vorhandenen Buchsen passen also nicht, nirgends lassen sich passende Buchsen auftreiben. Wieso hat KiCad Footprints, die zu keinen Komponenten passen?
Was nicht passt, wird passend gemacht, also müssen die Buchsen wohl etwas bearbeitet werden.


Passend gemachte Buchse. Der vordere Pin wurde abgeknipst, und der mittlere Pin etwas nach vorne gebogen.

Nach erfolgreichem Test hat sich dann ein weiteres Problem offenbart.


Irgendwie wirken die Farben vertauscht

Dadurch, dass der V9958 seine Video-Ausgabepins für die Farbkanäle nicht in der Reihenfolge RGB, sondern GRB angeordnet hat, sind im Schaltplan Rot und Blau verwechselt worden. Glücklicherweise gehen die Farbsignale über Koppelkondensatoren an den CXA2075M, sodass der Fix erstmal darin besteht, C9 und C12 über Kreuz einzulöten.


Das Board mit Cinch-Buchsen und Farb-Fix. Fehlt noch die S-Video-Buchse

Oh, und es soll nicht unerwähnt bleiben, dass wir uns erstmals getraut haben, mit dem CXA2075M einen SMD-Chip zu verbauen.

Posted in 1084S, 9918, FBAS, PAL, RGB, SCART, tms9929, V9958, vdp, video, video chip, video signal | Tagged , , ,

V9958 – Vom Steckbrett zum Prototypen

Es ist soweit, der erste Prototyp auf Platine von unserem neuen V9958-Videoboard ist fertig layoutet, und die Platinen warten beim Fertiger auf ihren Versand.

Der Steckbrettaufbau vom letzten Post wurde auf 128k erweitert. Dazu ist einfach eine 2. Bank von 2 Stück 64k x 4 DRAMS dazugebaut worden. Damit entspricht der Aufbau unserem Schaltplan, und dieser ist damit getestet.

Unsere Timing-Probleme führen wir auf zu kurze Delays bei Registerzugriffen, ein Softwareproblem also, zurück. Das Businterface als solches ist durchaus sauber, und wir haben es genauso gelassen wie beim TMS9929. Einziger Unterschied ist, dass /CSW und /CSR nicht mehr mit einem 7400 erzeugt werden, sondern mit einem 74139. Damit sparen wir uns später einen IC, wenn wir aus dem Videoboard ein kombiniertes Video- und Soundboard machen, indem wir den OPL2-Chip noch dazupacken. Aber wir greifen vor.

Das neue V9958-Board markiert auch eine Veränderung in unserer Vorgehensweise. Bislang haben wir Neuerungen immer komplett ausdesigned auf dem Steckbrett aufgebaut, und erst ganz am Schluss Platinen fertigen lassen, haben wir hier bewusst erst nur die Basisversion layoutet und direkt als Platinen fertigen lassen. Die günstigen Preise der diversen Platinenfertiger machens möglich, und wir verbringen weniger Zeit mit nervtötenden, wackligen Steckbrettaufbauten, und können uns auch langwierige und fehlerträchtige Aufbauten auf Lochrasterplatine sparen. Als angenehmen Nebeneffekt haben wir zwingend einen Schaltplan für jeden Schritt erstellt und haben somit alles lückenlos dokumentiert. Wir rechnen mit noch mindestens 2 Revisionen des Boards. Nämlich eine Reduktion der DRAM-Bestückung auf einen einzigen 16bit-Baustein, und zum Schluss Hinzufügen des Soundchips, sodass das Steckschwein in Zukunft nur noch aus 3 Platinen besteht. Jedenfalls bis wir uns dann an den Einplatiner wagen.


Der Steckbrettaufbau mit 128k. Für das Bild im Hintergrund werden trotzdem nur 16k verwendet.


3D-Rendering der neuen Platine

Posted in Allgemein, dram, platinen, schaltplan, V9958, video, video chip, video signal | 2 Comments

Meltdown / Spectre

Aufgrund der verwendeten überlegenen Prozessortechnologie ist das Steckschwein nicht von den aktuellen Prozessor-Sicherheitslücken Meltdown bzw. Spectre betroffen.

Posted in Allgemein, cpu

Es wird wieder gesteckt

Wir haben uns schon länger ein Upgrade des Videochips des Steckschweins vorgenommen. Der TMS9929 ist ein netter Chip, aber an einem 8MHz-65c02, der dazu noch so coole Hardware-Features hat, fühlt er sich ein bisschen wie die Achillesferse an.

Zum Glück war beim TMS9929 nicht Schluss, denn dieser hat im Laufe der Zeit diverse Nachfolger bekommen, welche von Yamaha hergestellt wurden und in diversen Weiterentwicklungen des MSX-Standards Verwendung fanden.

Der direkte Nachfolger der TMS99xx-Reihe ist der V9938. Dieser kam 1984 raus, also ganze 7 Jahre nachdem der TMS9918/9929 erschienen ist, und hat dementsprechend auch einiges mehr drauf, z.B.:

  • ein 80×24 Zeichen-Textmodus
  • maximale Auflösung von 512 × 212 (16 Farben von 512)
  • 32 Sprites, davon max. 8 auf einer Rasterzeile
  • Hardwarebeschleunigtes Füllen, Linien ziehen, etc.
  • Vertikales Scrollregister

Der Nachfolger des V9938 wiederum ist der V9958 von 1988. Dieser hat gegenüber dem Vorgänger nur einige kleine Verbesserungen erhalten, und zwar unter anderem:

  • Horizontales Scrollregister
  • Hardwarebeschleunigtes Füllen, Linien ziehen, etc. auch in nicht-bitmap-Modi

Beide Chips können bis zu 192k DRAM adressieren, und zwar max. 128k Video-RAM + 64k Extended RAM. Es werden DRAMs in den Formaten 16Kx1b, 16Kx4b, 64Kx1b und 64Kx4b unterstützt.


Unser Testaufbau “begnügt” sich mit 2x 64Kx4b und damit insgesamt 64Kb Video RAM (der TMS9929 kann nur max. 16K). Verglichen mit dem TMS9929 funktioniert das DRAM-Interface des V9958 selbst auf dem Steckbrett so stabil, dass wir auf irgendwelche SRAM-basierten Lösungen verzichten können. Auch der weitere Aufbau ist eher übersichtlich. Da der V9958 direkt RGB liefert, ist keine aufwendige Aufbereitung des Videosignals nötig. Als Ausgangsstufe wird ein Sony CXA2075M eingesetzt, der nebenher auch S-Video und Composite erzeugt. Damit dürfte sich künftig die Zahl der Steckschwein-geeigneten Fernseher/Monitore drastisch erhöhen.

Jetzt bleiben noch einige Detailfragen des Businterface zu klären. Wie MrFossi1 schon festgestellt hat, lassen sich Datentransfers ins Videoram nicht mehr in hoher Geschwindigkeit durchführen, während der Videochip im Blank ist oder das Display deaktiviert. Beim TMS9929 war das möglich.

Der V9958 hingegen verfügt allerdings über einen ominösen /WAIT-Pin, dessen Funktion allerdings erst per Software aktiviert werden muss. Das Datenblatt erwähnt die Wait-Funktion nur kurz als Möglichkeit, Zugriffe aufs VRAM zu beschleunigen, schweigt sich dann aber aus. Hier gilt es zu forschen.

Posted in 9918, Allgemein, dram, PAL, RGB, SCART, tms9929, V9958, vdp, video, video chip, video signal | 5 Comments