{-----------------------------------------------------------------------+ | | | Recreation of WozMon for Gigatron TTL microcomputer | | | +-----------------------------------------------------------------------} gcl0x { 2018-06-12 (marcelk) Initial version 2018-06-15 (marcelk) Mimic behaviour of original much better, while avoiding spurious newlines to save vertical space. Gracefully allow self-invocation ("200R"). 2018-06-21 (marcelk) Simplifications ported back from Tiny BASIC 2018-06-22 (marcelk) Use the won space to echo user input in white 2018-07-29 (marcelk) Accept up to 25 characters from input (was 24) What works the same as the original: - Type hex address to examine memory contents - Type '.' and end address for block examine - Type ':' and data bytes for writing into memory - Type 'R' to start execution from last address - Multiple commands in one line - Any ASCII code below '.' is a blank - Too long input is flushed - Shows backslash prompt at error ('\') Gigatron-specific differences: - Welcome message is "*** WozMon" instead of error prompt ('\') - Can use lowercase as well as uppercase for hex letters - Delete shows effect on screen, no need for '_' (RUBOUT) - Always print address at start of line in block examine mode - Input buffer is bounded by the screen width - Executed code can go back to monitor with RET instruction ($ff) - Faster screen update, and cursor symbol instead of '@' - A bit more careful with emitting empty lines References: Online Apple 1 Emulator (Hit [RESET] to enter WozMon): https://www.scullinsteel.com/apple1/ Using The Woz Monitor: https://www.sbprojects.net/projects/apple1/wozmon.php Original MOS6502 source code: https://github.com/jefftranter/6502/tree/master/asm/wozmon https://www.sbprojects.com/projects/apple1/wozmon.txt } {-----------------------------------------------------------------------+ | RAM page 2 | +-----------------------------------------------------------------------} [def $2a# $2a# $20# $57# $6f# $7a# $4d# $6f# $6e# 0#] tmp= {** WozMon} $01df deek Pos= {Bottom character row in screen memory} {Slightly cheating with endianness} { Print ASCII character (32..127) on screen in 5x8 pixels } { Also advance cursor position } [def 82- {Map ASCII code to offset in font table} [if<0 50+ i= \font32up {ASCII 32..81} else i= \font82up] k= {ASCII 82..127} i 2<< i+ {Multiply by 5} k+ k= {Add to page address to reach bitmap data} \SYS_VDrawBits_134 \sysFn: {Prepare SYS calls} \sysArgs6; \sysArgs0: {Apply caller-defined colors} Pos \sysArgs4: {Position for character} $fe%= {Temporarily park return value on the stack} 6+ Pos= {Advance position by 6 pixels} 5 [do i= {Draw 5 vertical slices} k 0? \sysArgs2. {Get slice from ROM} 134!! {Invoke SYS function to draw pixels} k<++ \sysArgs4<++ {Advance to next slice} i 1- if>0loop] {Looping} $fe% {Return effective position} ret ] PrintChar= { Print a newline conditionally } [def Pos<, [if<>0 {If not at start already} \SYS_VDrawBits_134 \sysFn: {Prepare SYS call} $800 Pos<. {Go back to start} Pos+ [if<0 $0800] Pos= {Go down 8 lines and wrap around if needed} \sysArgs4: {sysArgs[4:5] is position on screen} \sysArgs2. {All-zero output pattern} [do 134!! {SYS call} \sysArgs4<++ {Advance to next slice} \sysArgs4, 160^ {Test for end of screen} if<>0loop] {Scroll up by modifying videoTable} $01ee i= {Last entry in video table} [do i, 120- [if<0 128^ else 8^] i. {Rotate by 8 in 8..127 range} i 2- i= {Previous entry in video table} $fe^ if<>0loop] {Until all done} ] ret ] CNewline= { Print byte as hexadecimal value } [def push tmp= {Save value} \SYS_LSRW4_50 \sysFn: {Prepare SYS call for shift} tmp 50!! {Shift right 4 bits} 10- [if>=0 7+] $3a+ {Convert to hex digit} PrintChar! tmp 15& {Low 4 bits} 10- [if>=0 7+] $3a+ {Convert to hex digit} PrintChar! pop ret ] PrintByte= $300 call {-----------------------------------------------------------------------+ | RAM page 3 | +-----------------------------------------------------------------------} $0300: { Process new input line } [def push $80 Buffer= {One before begin of buffer} CNewline! [do {NEXTITEM} Buffer<++ {Advance text pointer} Buffer, if<>0 {While not at end of buffer} $2e- if<0loop {Ignore everything below '.'} [if=0 1 Mode= loop] {'.' Set Block Examine mode and continue} $0c- [if=0 1- Mode= loop] {':' Set Store mode and continue} $18- [if=0 Examine! loop] {'R' "Run" and continue in case of return} 0 tmp= {Stays zero if we don't see any hex digit} [do {NEXTHEX} {Parsing hexadecimal number} Value= {Update partial result} 4<< i= {Next term for adding hex digit} Buffer, {Get character for hex test} Buffer<++ {Already advance text pointer} $30- {'0'} if>=0 {Bail out if out of range} 10- {'9'} [if<0 {Decimal digit} tmp= {Non-zero to mark hex digits found} 10+ {Map in 0..9 range} i+ {Add it to value} loop] {To NEXTHEX} 6- 31& {Map remainder to uppercase} {'A'} if>0 {Bail out if out of range} 7- {'F'} if<0 {Hex letter} tmp= {Non-zero to mark hex digits found} 16+ {Map in 10..15 range} i+ {Add it to value} loop] {To NEXTHEX} tmp [if<>0 {There is a value to process} Buffer 2- Buffer= {We advanced too much} Mode [if<0 {"Store"} Value Store. {Store value through store pointer} Store 1+ Store= {Advance store pointer} loop] {To NEXTITEM} [if=0 {"Examine"} CNewline! {Forces printing of new address} Value Store= {Also set store pointer} 1- Examine=] {Will print one value} [do {"Block Examine"} Examine Value- if<0 {Walk with Examine until past Value} Examine 1+ Examine= {Advance examine pointer} 7& [if=0 CNewline!] {If address mod 8 == 0 continue on next line} Pos<, [if=0 {If at start of line print address} 2 Pos<. {Tiny 2 pixel indent to be nice} Examine>, PrintByte! {Print high-byte of address} Examine<, PrintByte! {Print low-byte of address} $3a PrintChar!] {Print colon} Pos 4+ Pos= {Small 4 pixel space to fit on Gigatron screen} Examine, PrintByte! {Print data byte at address} loop] 0 Mode= {Back to Examine mode} loop] {To NEXTITEM} $5c PrintChar!] {Error prompt '\' and break loop} pop ret ] ProcessBuffer= Pos<++ {Force a non-zero low byte} $0f20 \sysArgs6: {Pen color yellow, background color blue} \sysArgs0. CNewline! {Now forced to scroll and clear last line} { Display '*' and welcome message } $2a [do PrintChar! tmp, tmp<++ if<>0loop] $0400 call {-----------------------------------------------------------------------+ | RAM page 4 | +-----------------------------------------------------------------------} $0400: { Input loop, assume some kind of ASCII keyboard is hooked up } [do {GETLINE} $81 Buffer= {Prepare for next input line} CNewline! [do {NEXTCHAR} 127 \sysArgs7. {Pen color white for user input} PrintChar! Pos= {Draw cursor} \serialRaw, [do {Wait for key change} tmp= \serialRaw, Buffer. tmp^ if=0 Buffer, loop] Buffer, 10^ if<>0 {Enter/return breaks NEXTCHAR loop} 117^ [if=0 {Delete pressed (10^127 == 117)} $20 PrintChar! Pos= {Remove cursor} Pos<, 6- [if>=0 {If not on first position} Pos<. {Step back} Buffer 1- Buffer=] {Also remove character from buffer} loop] {To NEXTCHAR} 96- if>=0loop {Ignore apparent unprintable garbage} Pos<, 150^ [if=0 {Arrived at position 25 (25*6 == 150)} $5c PrintChar! {Error indicator '\'} $400 call] {To GETLINE and discard too long input} Buffer, PrintChar! {Print accepted characters} Buffer<++ {Advance pointer, keeping the character} loop] {To NEXTCHAR} Buffer. {Terminate input with zero} Mode= {Start in examine mode} $20 PrintChar! {Remove and advance cursor} $0f \sysArgs7. {Pen color yellow for output} ProcessBuffer! loop] {To GETLINE}